From 6ee3257b57cdcf056ca43895aa91a901b0298e54 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sat, 27 Dec 2025 12:42:16 +0100 Subject: [PATCH 01/14] OpenGraph images --- src/branding/repository-open-graph.png | 3 + src/branding/repository-open-graph.svg | 143 +++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/branding/repository-open-graph.png create mode 100644 src/branding/repository-open-graph.svg diff --git a/src/branding/repository-open-graph.png b/src/branding/repository-open-graph.png new file mode 100644 index 0000000..b1cc8d1 --- /dev/null +++ b/src/branding/repository-open-graph.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d268ec7f6cd9acc1a9c9db815154d9f8d9484664d2fca65dacbaa6a85ab7d3c8 +size 36185 diff --git a/src/branding/repository-open-graph.svg b/src/branding/repository-open-graph.svg new file mode 100644 index 0000000..8a0e407 --- /dev/null +++ b/src/branding/repository-open-graph.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + NET + + + From b96be747786697e03834536e648e1701bb42ee0d Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sun, 25 Jan 2026 10:00:12 +0100 Subject: [PATCH 02/14] Dependency checking --- src/DebiaNetApp/Commands/DockerMenuCommand.cs | 6 +++ src/DebiaNetApp/ExitCodes.cs | 1 + .../Infrastructure/DependencyChecker.cs | 37 +++++++++++++++++++ src/DebiaNetApp/Program.cs | 8 +++- .../Properties/Resources.Designer.cs | 18 +++++++++ src/DebiaNetApp/Properties/Resources.resx | 6 +++ 6 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/DebiaNetApp/Infrastructure/DependencyChecker.cs diff --git a/src/DebiaNetApp/Commands/DockerMenuCommand.cs b/src/DebiaNetApp/Commands/DockerMenuCommand.cs index bc41e71..0e61490 100644 --- a/src/DebiaNetApp/Commands/DockerMenuCommand.cs +++ b/src/DebiaNetApp/Commands/DockerMenuCommand.cs @@ -12,6 +12,12 @@ internal sealed class DockerMenuCommand : AsyncCommand { public override async Task ExecuteAsync(CommandContext context, CancellationToken cancellationToken) { + if (!DependencyChecker.IsDockerInstalled()) + { + Ui.Error(Resources.Error_DockerNotInstalled); + return ExitCodes.MissingDependency; + } + var selections = new SelectorMenuItem[] { new JsonCliMenuItem(Resources.Menu_Docker_ListRunningContainers, $"{Emoji.Known.Scroll} ") diff --git a/src/DebiaNetApp/ExitCodes.cs b/src/DebiaNetApp/ExitCodes.cs index a5ae340..bed6dcf 100644 --- a/src/DebiaNetApp/ExitCodes.cs +++ b/src/DebiaNetApp/ExitCodes.cs @@ -5,4 +5,5 @@ internal static class ExitCodes public const int NotLinux = ushort.MaxValue; public const int SudoUser = 1; public const int Success = 0; + public const int MissingDependency = 2; } \ No newline at end of file diff --git a/src/DebiaNetApp/Infrastructure/DependencyChecker.cs b/src/DebiaNetApp/Infrastructure/DependencyChecker.cs new file mode 100644 index 0000000..d035d61 --- /dev/null +++ b/src/DebiaNetApp/Infrastructure/DependencyChecker.cs @@ -0,0 +1,37 @@ +using System.Diagnostics; + +namespace DebiaNetApp.Infrastructure; + +internal static class DependencyChecker +{ + public static bool IsInstalled(string appBinary, params string[] arguments) + { + try + { + using var process = new Process(); + process.StartInfo.FileName = appBinary; + for (int i = 0; i < arguments.Length; i++) + { + process.StartInfo.ArgumentList.Add(arguments[i]); + } + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + + process.Start(); + process.WaitForExit(); + return process.ExitCode == 0; + } + catch + { + return false; + } + } + + public static bool IsDockerInstalled() + => IsInstalled("docker", "--version"); + + public static bool IsAptAvailable() + => IsInstalled("apt-get", "--version"); +} diff --git a/src/DebiaNetApp/Program.cs b/src/DebiaNetApp/Program.cs index c637d70..61c00b9 100644 --- a/src/DebiaNetApp/Program.cs +++ b/src/DebiaNetApp/Program.cs @@ -17,7 +17,13 @@ if (Linux.IsRunningWithElevatedPriviliges()) { Ui.Error(Resources.Error_SudoUser); - return ExitCodes.NotLinux; + return ExitCodes.SudoUser; +} + +if (!DependencyChecker.IsAptAvailable()) +{ + Ui.Error(Resources.Error_NotAptDistro); + return ExitCodes.MissingDependency; } var app = new CommandApp(); diff --git a/src/DebiaNetApp/Properties/Resources.Designer.cs b/src/DebiaNetApp/Properties/Resources.Designer.cs index 0ee05aa..6ffa6cd 100644 --- a/src/DebiaNetApp/Properties/Resources.Designer.cs +++ b/src/DebiaNetApp/Properties/Resources.Designer.cs @@ -60,6 +60,24 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to This function of the app requires docker to be installed.. + /// + public static string Error_DockerNotInstalled { + get { + return ResourceManager.GetString("Error_DockerNotInstalled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your distribution doesn't use apt as package manager. This app only supports apt based distributions. + /// + public static string Error_NotAptDistro { + get { + return ResourceManager.GetString("Error_NotAptDistro", resourceCulture); + } + } + /// /// Looks up a localized string similar to This application is only supported on Linux systems.. /// diff --git a/src/DebiaNetApp/Properties/Resources.resx b/src/DebiaNetApp/Properties/Resources.resx index 4ada2bc..8c42f75 100644 --- a/src/DebiaNetApp/Properties/Resources.resx +++ b/src/DebiaNetApp/Properties/Resources.resx @@ -186,4 +186,10 @@ Value + + This function of the app requires docker to be installed. + + + Your distribution doesn't use apt as package manager. This app only supports apt based distributions + \ No newline at end of file From ea14121c7ca09f3122548791bab15f128de2b7f0 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sun, 25 Jan 2026 10:05:02 +0100 Subject: [PATCH 03/14] Package as tool --- src/DebiaNetApp/DebiaNetApp.csproj | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/DebiaNetApp/DebiaNetApp.csproj b/src/DebiaNetApp/DebiaNetApp.csproj index e71d386..55f2457 100644 --- a/src/DebiaNetApp/DebiaNetApp.csproj +++ b/src/DebiaNetApp/DebiaNetApp.csproj @@ -6,6 +6,16 @@ enable enable true + Webmaster442 + https://github.com/webmaster442/DebiaNet + https://github.com/webmaster442/DebiaNet + git + True + debianet + True + + $([System.DateTime]::UtcNow.ToString("yyyy")).$([System.DateTime]::UtcNow.ToString("MM")).$([System.DateTime]::UtcNow.ToString("dd")).0 + From 567d97705d6dcb6500634912768771e10b00c58b Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Mon, 2 Feb 2026 22:50:08 +0100 Subject: [PATCH 04/14] added just --- readme.md | 1 + src/install.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/readme.md b/readme.md index 3995a1a..9e53856 100644 --- a/readme.md +++ b/readme.md @@ -34,6 +34,7 @@ The complete list of changes can be found here: [changelog](changelog.md) - [FastFetch](https://github.com/fastfetch-cli/fastfetch) - [git](https://git-scm.com/) with [git-lfs](https://git-lfs.com/) - [htop](https://htop.dev/) +- [just](https://github.com/casey/just) - [lazygit](https://github.com/jesseduffield/lazygit) - [openssl](https://www.openssl.org/) - [ripgrep](https://github.com/BurntSushi/ripgrep) diff --git a/src/install.sh b/src/install.sh index 47f26f5..a6d9960 100644 --- a/src/install.sh +++ b/src/install.sh @@ -23,6 +23,7 @@ sudo apt install -y \ git-lfs \ gpg \ htop \ + just \ lazygit \ libxml2 \ mc \ From 4a961d32fc38a124cd01dc71624a8943973dd89d Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sat, 4 Apr 2026 18:54:50 +0200 Subject: [PATCH 05/14] Update versions and install script --- changelog.md | 10 ++++++++++ readme.md | 26 +++++++++++++++++--------- src/install.sh | 17 +++++++++++++---- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/changelog.md b/changelog.md index ffb8170..57936ab 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,14 @@ # Changelog +## 2026.04.04 - Version 26.04 +- Based on Debian +- Added byobu to default install +- Added just to default install +- Added jq to install +- Added Infer# to install +- .NET 8.0.25 +- .NET 9.0.14 +- .NET 10.0.5 + ## 2025.12.27 - Initial release - Initial 'beta' release for testing \ No newline at end of file diff --git a/readme.md b/readme.md index 9e53856..9a02c4c 100644 --- a/readme.md +++ b/readme.md @@ -40,8 +40,14 @@ The complete list of changes can be found here: [changelog](changelog.md) - [ripgrep](https://github.com/BurntSushi/ripgrep) - [strace](https://strace.io/) - [tmux](https://github.com/tmux/tmux/wiki) -- [Visual studio remote shell](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging?view=visualstudio) - [wget](https://www.gnu.org/software/wget/) +- [jq](https://jqlang.org/) +- [byobu](https://www.byobu.org/) + +**Apps** + +- [Visual studio remote shell](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging?view=visualstudio) +- [Infer#](https://github.com/microsoft/infersharp) **.NET tools from microsoft** @@ -54,7 +60,7 @@ The complete list of changes can be found here: [changelog](changelog.md) - [dotnet-symbol](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-symbol) - [dotnet-trace](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace) - [powershell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-linux) -- [sngen](https://github.com/microsoft/slngen) +- [slngen](https://github.com/microsoft/slngen) - [upgrade-assistant](https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview) - [DocFx](https://dotnet.github.io/docfx/index.html) @@ -63,6 +69,7 @@ The complete list of changes can be found here: [changelog](changelog.md) - [csharprepl](https://github.com/waf/CSharpRepl) - [ilspycmd](github.com/icsharpcode/ILSpy) - [roslynator](https://github.com/dotnet/roslynator?tab=readme-ov-file#command-line-tool) +- [Csproj](https://github.com/webmaster442/csproj) ## Installation @@ -89,10 +96,11 @@ wsl --set-default-version 2 1. Install debian: `wsl --install Debian` 2. Install user: `user` with password: `pass` 3. Run install.sh: `./src/install.sh` this will do most of the installing of software -4. Run branding.sh: `sudo ./src/branding.sh` to do branding -5. Run install-app.sh: `sudo ./src/install-app.sh` to install the debianet app. -5. exit: `exit` -6. do a shutdown: `wsl --shutdown` -7. export: `wsl --export Debian --format tar.xz debiannet.wsl` -8. unregister debian: `wsl --unregister Debian` -9. reinstall debianet.wsl \ No newline at end of file +4. Change to the source directory: `cd src` +5. Run branding.sh: `sudo ./branding.sh` to do branding +6. Run install-app.sh: `sudo ./install-app.sh` to install the debianet app. +7. exit: `exit` +8. do a shutdown: `wsl --shutdown` +9. export: `wsl --export Debian --format tar.xz debiannet.wsl` +10. unregister debian: `wsl --unregister Debian` +11. reinstall debianet.wsl \ No newline at end of file diff --git a/src/install.sh b/src/install.sh index a6d9960..63dccfb 100644 --- a/src/install.sh +++ b/src/install.sh @@ -32,7 +32,9 @@ sudo apt install -y \ strace \ tmux \ unzip \ - wget + wget \ + jq \ + byobu # prerpare docker install sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc @@ -66,9 +68,6 @@ sudo usermod -aG docker $USER sudo mkdir -p /etc/systemd/system/docker.service.d/ sudo cp docker-override.conf /etc/systemd/system/docker.service.d/override.conf -# update dotnet workloads -sudo dotnet workload update - # install devtoys.cli wget https://github.com/DevToys-app/DevToys/releases/download/v2.0.8.0/devtoys.cli_linux_x64.deb -O devtoys.cli.deb sudo dpkg -i devtoys.cli.deb @@ -77,6 +76,12 @@ rm devtoys.cli.deb # install VS Remote debugger curl -sSL https://aka.ms/getvsdbgsh | /bin/sh /dev/stdin -v latest -l ~/vsdbg +# install infer# +mkdir -p ~/infersharp +curl -L https://github.com/microsoft/infersharp/releases/download/v1.5/infersharp-linux64-v1.5.tar.gz -o infersharp.tar.gz +tar -xvzf infersharp.tar.gz -C ~/infersharp +rm infersharp.tar.gz + # install ripgrep wget https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep_15.1.0-1_amd64.deb -O ripgrep.deb sudo dpkg -i ripgrep.deb @@ -88,6 +93,9 @@ curl -fOL "https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/ sudo apt install ./dive_${DIVE_VERSION}_linux_amd64.deb rm ./dive_${DIVE_VERSION}_linux_amd64.deb +# update dotnet workloads +sudo dotnet workload update + # install .NET Global tools dotnet tool install --global dotnet-counters dotnet tool install --global dotnet-coverage @@ -106,6 +114,7 @@ dotnet tool install --global docfx dotnet tool install --global csharprepl dotnet tool install --global ilspycmd dotnet tool install --global roslynator.dotnet.cli +dotnet tool install --global CsProj # set path for dotnet tools echo 'export PATH="$PATH:$HOME/.dotnet/tools"' >> ~/.bashrc From eb88108016a393e0c322b8da91a5adf67b39c52b Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Thu, 11 Jun 2026 18:47:21 +0200 Subject: [PATCH 06/14] Begining to rework app --- {src => Trash}/DebiaNetApp/.editorconfig | 0 {src => Trash}/DebiaNetApp/.gitignore | 0 .../DebiaNetApp/Commands/DockerMenuCommand.cs | 0 .../DebiaNetApp/Commands/MainMenuCommand.cs | 0 {src => Trash}/DebiaNetApp/DebiaNetApp.csproj | 0 .../DebiaNetApp/Dto/DockerContainer.cs | 0 {src => Trash}/DebiaNetApp/Dto/DockerImage.cs | 0 .../DebiaNetApp/Dto/DockerPlatform.cs | 0 {src => Trash}/DebiaNetApp/Dto/DockerPs.cs | 0 {src => Trash}/DebiaNetApp/ExitCodes.cs | 0 .../DebiaNetApp/Infrastructure/CliCommand.cs | 0 .../DebiaNetApp/Infrastructure/CliMenuItem.cs | 0 .../Infrastructure/DebugInterceptor.cs | 0 .../Infrastructure/DelegateMenuItem.cs | 0 .../Infrastructure/DependencyChecker.cs | 0 .../Infrastructure/JsonCliMenuItem.cs | 0 .../DebiaNetApp/Infrastructure/Linux.cs | 0 .../Infrastructure/SelectorMenuItem.cs | 0 .../DebiaNetApp/Infrastructure/Ui.cs | 0 {src => Trash}/DebiaNetApp/Program.cs | 0 .../Properties/Resources.Designer.cs | 0 .../DebiaNetApp/Properties/Resources.resx | 0 {src => Trash}/DebiaNetApp/Tasks.cs | 0 src/App/.editorconfig | 389 ++++++++++++++ src/App/.gitignore | 482 ++++++++++++++++++ src/App/Abstractions/Application.cs | 141 +++++ src/App/Abstractions/IApplication.cs | 8 + src/App/Abstractions/ITerminal.cs | 17 + src/App/Abstractions/Icons.cs | 13 + src/App/Abstractions/Palete.cs | 21 + src/App/Abstractions/Terminal.cs | 52 ++ src/App/Debianet.csproj | 34 ++ src/App/Debianet.slnx | 3 + src/App/MenuRegistry.cs | 16 + src/App/Menus/DotnetMenu.cs | 81 +++ src/App/Menus/MainMenu.cs | 66 +++ src/App/Program.cs | 8 + src/App/Properties/Resources.Designer.cs | 171 +++++++ src/App/Properties/Resources.resx | 156 ++++++ src/App/Ui/DelegateMenuItem.cs | 14 + src/App/Ui/Menu.cs | 20 + src/App/Ui/MenuItemBase.cs | 10 + src/App/Ui/ReplaceMenuMenuItem.cs | 13 + src/App/Ui/RunCommandMenuItem.cs | 45 ++ src/branding/DebiaNet_2.png | 3 + src/branding/debianet2.svg | 137 +++++ src/install.sh | 3 + 47 files changed, 1903 insertions(+) rename {src => Trash}/DebiaNetApp/.editorconfig (100%) rename {src => Trash}/DebiaNetApp/.gitignore (100%) rename {src => Trash}/DebiaNetApp/Commands/DockerMenuCommand.cs (100%) rename {src => Trash}/DebiaNetApp/Commands/MainMenuCommand.cs (100%) rename {src => Trash}/DebiaNetApp/DebiaNetApp.csproj (100%) rename {src => Trash}/DebiaNetApp/Dto/DockerContainer.cs (100%) rename {src => Trash}/DebiaNetApp/Dto/DockerImage.cs (100%) rename {src => Trash}/DebiaNetApp/Dto/DockerPlatform.cs (100%) rename {src => Trash}/DebiaNetApp/Dto/DockerPs.cs (100%) rename {src => Trash}/DebiaNetApp/ExitCodes.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/CliCommand.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/CliMenuItem.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/DebugInterceptor.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/DelegateMenuItem.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/DependencyChecker.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/JsonCliMenuItem.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/Linux.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/SelectorMenuItem.cs (100%) rename {src => Trash}/DebiaNetApp/Infrastructure/Ui.cs (100%) rename {src => Trash}/DebiaNetApp/Program.cs (100%) rename {src => Trash}/DebiaNetApp/Properties/Resources.Designer.cs (100%) rename {src => Trash}/DebiaNetApp/Properties/Resources.resx (100%) rename {src => Trash}/DebiaNetApp/Tasks.cs (100%) create mode 100644 src/App/.editorconfig create mode 100644 src/App/.gitignore create mode 100644 src/App/Abstractions/Application.cs create mode 100644 src/App/Abstractions/IApplication.cs create mode 100644 src/App/Abstractions/ITerminal.cs create mode 100644 src/App/Abstractions/Icons.cs create mode 100644 src/App/Abstractions/Palete.cs create mode 100644 src/App/Abstractions/Terminal.cs create mode 100644 src/App/Debianet.csproj create mode 100644 src/App/Debianet.slnx create mode 100644 src/App/MenuRegistry.cs create mode 100644 src/App/Menus/DotnetMenu.cs create mode 100644 src/App/Menus/MainMenu.cs create mode 100644 src/App/Program.cs create mode 100644 src/App/Properties/Resources.Designer.cs create mode 100644 src/App/Properties/Resources.resx create mode 100644 src/App/Ui/DelegateMenuItem.cs create mode 100644 src/App/Ui/Menu.cs create mode 100644 src/App/Ui/MenuItemBase.cs create mode 100644 src/App/Ui/ReplaceMenuMenuItem.cs create mode 100644 src/App/Ui/RunCommandMenuItem.cs create mode 100644 src/branding/DebiaNet_2.png create mode 100644 src/branding/debianet2.svg diff --git a/src/DebiaNetApp/.editorconfig b/Trash/DebiaNetApp/.editorconfig similarity index 100% rename from src/DebiaNetApp/.editorconfig rename to Trash/DebiaNetApp/.editorconfig diff --git a/src/DebiaNetApp/.gitignore b/Trash/DebiaNetApp/.gitignore similarity index 100% rename from src/DebiaNetApp/.gitignore rename to Trash/DebiaNetApp/.gitignore diff --git a/src/DebiaNetApp/Commands/DockerMenuCommand.cs b/Trash/DebiaNetApp/Commands/DockerMenuCommand.cs similarity index 100% rename from src/DebiaNetApp/Commands/DockerMenuCommand.cs rename to Trash/DebiaNetApp/Commands/DockerMenuCommand.cs diff --git a/src/DebiaNetApp/Commands/MainMenuCommand.cs b/Trash/DebiaNetApp/Commands/MainMenuCommand.cs similarity index 100% rename from src/DebiaNetApp/Commands/MainMenuCommand.cs rename to Trash/DebiaNetApp/Commands/MainMenuCommand.cs diff --git a/src/DebiaNetApp/DebiaNetApp.csproj b/Trash/DebiaNetApp/DebiaNetApp.csproj similarity index 100% rename from src/DebiaNetApp/DebiaNetApp.csproj rename to Trash/DebiaNetApp/DebiaNetApp.csproj diff --git a/src/DebiaNetApp/Dto/DockerContainer.cs b/Trash/DebiaNetApp/Dto/DockerContainer.cs similarity index 100% rename from src/DebiaNetApp/Dto/DockerContainer.cs rename to Trash/DebiaNetApp/Dto/DockerContainer.cs diff --git a/src/DebiaNetApp/Dto/DockerImage.cs b/Trash/DebiaNetApp/Dto/DockerImage.cs similarity index 100% rename from src/DebiaNetApp/Dto/DockerImage.cs rename to Trash/DebiaNetApp/Dto/DockerImage.cs diff --git a/src/DebiaNetApp/Dto/DockerPlatform.cs b/Trash/DebiaNetApp/Dto/DockerPlatform.cs similarity index 100% rename from src/DebiaNetApp/Dto/DockerPlatform.cs rename to Trash/DebiaNetApp/Dto/DockerPlatform.cs diff --git a/src/DebiaNetApp/Dto/DockerPs.cs b/Trash/DebiaNetApp/Dto/DockerPs.cs similarity index 100% rename from src/DebiaNetApp/Dto/DockerPs.cs rename to Trash/DebiaNetApp/Dto/DockerPs.cs diff --git a/src/DebiaNetApp/ExitCodes.cs b/Trash/DebiaNetApp/ExitCodes.cs similarity index 100% rename from src/DebiaNetApp/ExitCodes.cs rename to Trash/DebiaNetApp/ExitCodes.cs diff --git a/src/DebiaNetApp/Infrastructure/CliCommand.cs b/Trash/DebiaNetApp/Infrastructure/CliCommand.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/CliCommand.cs rename to Trash/DebiaNetApp/Infrastructure/CliCommand.cs diff --git a/src/DebiaNetApp/Infrastructure/CliMenuItem.cs b/Trash/DebiaNetApp/Infrastructure/CliMenuItem.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/CliMenuItem.cs rename to Trash/DebiaNetApp/Infrastructure/CliMenuItem.cs diff --git a/src/DebiaNetApp/Infrastructure/DebugInterceptor.cs b/Trash/DebiaNetApp/Infrastructure/DebugInterceptor.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/DebugInterceptor.cs rename to Trash/DebiaNetApp/Infrastructure/DebugInterceptor.cs diff --git a/src/DebiaNetApp/Infrastructure/DelegateMenuItem.cs b/Trash/DebiaNetApp/Infrastructure/DelegateMenuItem.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/DelegateMenuItem.cs rename to Trash/DebiaNetApp/Infrastructure/DelegateMenuItem.cs diff --git a/src/DebiaNetApp/Infrastructure/DependencyChecker.cs b/Trash/DebiaNetApp/Infrastructure/DependencyChecker.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/DependencyChecker.cs rename to Trash/DebiaNetApp/Infrastructure/DependencyChecker.cs diff --git a/src/DebiaNetApp/Infrastructure/JsonCliMenuItem.cs b/Trash/DebiaNetApp/Infrastructure/JsonCliMenuItem.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/JsonCliMenuItem.cs rename to Trash/DebiaNetApp/Infrastructure/JsonCliMenuItem.cs diff --git a/src/DebiaNetApp/Infrastructure/Linux.cs b/Trash/DebiaNetApp/Infrastructure/Linux.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/Linux.cs rename to Trash/DebiaNetApp/Infrastructure/Linux.cs diff --git a/src/DebiaNetApp/Infrastructure/SelectorMenuItem.cs b/Trash/DebiaNetApp/Infrastructure/SelectorMenuItem.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/SelectorMenuItem.cs rename to Trash/DebiaNetApp/Infrastructure/SelectorMenuItem.cs diff --git a/src/DebiaNetApp/Infrastructure/Ui.cs b/Trash/DebiaNetApp/Infrastructure/Ui.cs similarity index 100% rename from src/DebiaNetApp/Infrastructure/Ui.cs rename to Trash/DebiaNetApp/Infrastructure/Ui.cs diff --git a/src/DebiaNetApp/Program.cs b/Trash/DebiaNetApp/Program.cs similarity index 100% rename from src/DebiaNetApp/Program.cs rename to Trash/DebiaNetApp/Program.cs diff --git a/src/DebiaNetApp/Properties/Resources.Designer.cs b/Trash/DebiaNetApp/Properties/Resources.Designer.cs similarity index 100% rename from src/DebiaNetApp/Properties/Resources.Designer.cs rename to Trash/DebiaNetApp/Properties/Resources.Designer.cs diff --git a/src/DebiaNetApp/Properties/Resources.resx b/Trash/DebiaNetApp/Properties/Resources.resx similarity index 100% rename from src/DebiaNetApp/Properties/Resources.resx rename to Trash/DebiaNetApp/Properties/Resources.resx diff --git a/src/DebiaNetApp/Tasks.cs b/Trash/DebiaNetApp/Tasks.cs similarity index 100% rename from src/DebiaNetApp/Tasks.cs rename to Trash/DebiaNetApp/Tasks.cs diff --git a/src/App/.editorconfig b/src/App/.editorconfig new file mode 100644 index 0000000..af3d700 --- /dev/null +++ b/src/App/.editorconfig @@ -0,0 +1,389 @@ +root = true + +# All files +[*] +indent_style = space + +# Xml files +[*.xml] +indent_size = 2 + +# Xml project files +[*.{csproj,fsproj,vbproj,proj,slnx}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +[*.json] +indent_size = 2 + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +tab_width = 4 + +# New line preferences +insert_final_newline = false + +#### .NET Coding Conventions #### +[*.{cs,vb}] + +# Organize usings +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = true +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:warning + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# Coding Conventions #### +[*.cs] + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_anonymous_function = true:suggestion +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### +[*.{cs,vb}] + +# Naming rules + +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion +dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces +dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase + +dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion +dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters +dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase + +dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods +dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties +dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.events_should_be_pascalcase.symbols = events +dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables +dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase + +dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants +dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase + +dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion +dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters +dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase + +dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields +dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion +dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields +dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase + +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase + +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums +dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase + +# Symbol specifications + +dotnet_naming_symbols.interfaces.applicable_kinds = interface +dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interfaces.required_modifiers = + +dotnet_naming_symbols.enums.applicable_kinds = enum +dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.enums.required_modifiers = + +dotnet_naming_symbols.events.applicable_kinds = event +dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.events.required_modifiers = + +dotnet_naming_symbols.methods.applicable_kinds = method +dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.methods.required_modifiers = + +dotnet_naming_symbols.properties.applicable_kinds = property +dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.properties.required_modifiers = + +dotnet_naming_symbols.public_fields.applicable_kinds = field +dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_fields.required_modifiers = + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_fields.required_modifiers = + +dotnet_naming_symbols.private_static_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_static_fields.required_modifiers = static + +dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum +dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types_and_namespaces.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.type_parameters.applicable_kinds = namespace +dotnet_naming_symbols.type_parameters.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters.required_modifiers = + +dotnet_naming_symbols.private_constant_fields.applicable_kinds = field +dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_constant_fields.required_modifiers = const + +dotnet_naming_symbols.local_variables.applicable_kinds = local +dotnet_naming_symbols.local_variables.applicable_accessibilities = local +dotnet_naming_symbols.local_variables.required_modifiers = + +dotnet_naming_symbols.local_constants.applicable_kinds = local +dotnet_naming_symbols.local_constants.applicable_accessibilities = local +dotnet_naming_symbols.local_constants.required_modifiers = const + +dotnet_naming_symbols.parameters.applicable_kinds = parameter +dotnet_naming_symbols.parameters.applicable_accessibilities = * +dotnet_naming_symbols.parameters.required_modifiers = + +dotnet_naming_symbols.public_constant_fields.applicable_kinds = field +dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_constant_fields.required_modifiers = const + +dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function +dotnet_naming_symbols.local_functions.applicable_accessibilities = * +dotnet_naming_symbols.local_functions.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascalcase.required_prefix = +dotnet_naming_style.pascalcase.required_suffix = +dotnet_naming_style.pascalcase.word_separator = +dotnet_naming_style.pascalcase.capitalization = pascal_case + +dotnet_naming_style.ipascalcase.required_prefix = I +dotnet_naming_style.ipascalcase.required_suffix = +dotnet_naming_style.ipascalcase.word_separator = +dotnet_naming_style.ipascalcase.capitalization = pascal_case + +dotnet_naming_style.tpascalcase.required_prefix = T +dotnet_naming_style.tpascalcase.required_suffix = +dotnet_naming_style.tpascalcase.word_separator = +dotnet_naming_style.tpascalcase.capitalization = pascal_case + +dotnet_naming_style._camelcase.required_prefix = _ +dotnet_naming_style._camelcase.required_suffix = +dotnet_naming_style._camelcase.word_separator = +dotnet_naming_style._camelcase.capitalization = camel_case + +dotnet_naming_style.camelcase.required_prefix = +dotnet_naming_style.camelcase.required_suffix = +dotnet_naming_style.camelcase.word_separator = +dotnet_naming_style.camelcase.capitalization = camel_case + +dotnet_naming_style.s_camelcase.required_prefix = s_ +dotnet_naming_style.s_camelcase.required_suffix = +dotnet_naming_style.s_camelcase.word_separator = +dotnet_naming_style.s_camelcase.capitalization = camel_case + diff --git a/src/App/.gitignore b/src/App/.gitignore new file mode 100644 index 0000000..0808c4a --- /dev/null +++ b/src/App/.gitignore @@ -0,0 +1,482 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea/ + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/src/App/Abstractions/Application.cs b/src/App/Abstractions/Application.cs new file mode 100644 index 0000000..0b56b9f --- /dev/null +++ b/src/App/Abstractions/Application.cs @@ -0,0 +1,141 @@ +using Debianet.Ui; + +using Spectre.Console; + +namespace Debianet.Abstractions; + +internal sealed class Application : IApplication +{ + private sealed class ConsoleCancellationTokenSource : IDisposable + { + private readonly CancellationTokenSource _cancellationTokenSource; + + public ConsoleCancellationTokenSource() + { + _cancellationTokenSource = new CancellationTokenSource(); + Console.CancelKeyPress += OnCancelKeyPress; + } + + public CancellationToken Token + => _cancellationTokenSource.Token; + + private void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) + { + _cancellationTokenSource.Cancel(); + e.Cancel = true; + } + + public void Dispose() + { + Console.CancelKeyPress -= OnCancelKeyPress; + _cancellationTokenSource.Dispose(); + } + } + + private readonly Stack _menuStack; + private readonly ITerminal _terminal; + private readonly Menu _mainMenu; + private readonly Dictionary _icons; + private Menu? _currentMenu; + + public Application(ITerminal terminal, MenuRegistry menuRegistry) + { + _menuStack = new Stack(); + _terminal = terminal; + _mainMenu = menuRegistry.Main; + _icons = new Dictionary + { + { 'a', "🅰" }, + { 'b', "🅱" }, + { 'c', "🅲" }, + { 'd', "🅳" }, + { 'e', "🅴" }, + { 'f', "🅵" }, + { 'g', "🅶" }, + { 'h', "🅷" }, + { 'i', "🅸" }, + { 'j', "🅹" }, + { 'k', "🅺" }, + { 'l', "🅻" }, + { 'm', "🅼" }, + { 'n', "🅽" }, + { 'o', "🅾" }, + { 'p', "🅿" }, + { 'q', "🆀" }, + { 'r', "🆁" }, + { 's', "🆂" }, + { 't', "🆃" }, + { 'u', "🆄" }, + { 'v', "🆅" }, + { 'w', "🆆" }, + { 'x', "🆇" }, + { 'y', "🆈" }, + { 'z', "🆉" } + }; + } + + private Menu GetMenu() + { + return _menuStack.Count > 0 + ? _menuStack.Pop() + : _mainMenu; + } + + public void SwitchMenu(Menu menu) + { + if (_currentMenu != null) + { + _menuStack.Push(_currentMenu); + } + _menuStack.Push(menu); + } + + public async Task Run() + { + Console.OutputEncoding = System.Text.Encoding.UTF8; + + while (true) + { + _currentMenu = GetMenu(); + _terminal.SwitchToAlternateBuffer(); + _terminal.Clear(); + try + { + _currentMenu.BeforeSelection(); + var prompt = new SelectionPrompt() + .Title(_currentMenu.Title) + .UseConverter(MenuitemConverter) + .AddChoices(_currentMenu.Items); + + var grid = new Grid(); + grid.AddColumn(); + grid.AddColumn(); + + + prompt.SearchEnabled = true; + var selection = AnsiConsole.Prompt(prompt); + + using (var tokenSource = new ConsoleCancellationTokenSource()) + { + _terminal.SwitchToMainBuffer(); + _terminal.Clear(); + await selection.Execute(this, tokenSource.Token); + } + } + catch (Exception ex) + { + _terminal.DisplayException(ex); + } + } + } + + private string MenuitemConverter(MenuItemBase item) + { + if (item.Icon != null) + { + return $"{item.Icon} {item.Text}"; + } + var firstChar = char.ToLower(item.Text[0]); + return $"{_icons[firstChar]} {item.Text}"; + } +} diff --git a/src/App/Abstractions/IApplication.cs b/src/App/Abstractions/IApplication.cs new file mode 100644 index 0000000..3a17c3a --- /dev/null +++ b/src/App/Abstractions/IApplication.cs @@ -0,0 +1,8 @@ +using Debianet.Ui; + +namespace Debianet.Abstractions; + +internal interface IApplication +{ + void SwitchMenu(Menu menu); +} diff --git a/src/App/Abstractions/ITerminal.cs b/src/App/Abstractions/ITerminal.cs new file mode 100644 index 0000000..e92720f --- /dev/null +++ b/src/App/Abstractions/ITerminal.cs @@ -0,0 +1,17 @@ +namespace Debianet.Abstractions; + +internal interface ITerminal +{ + void SwitchToAlternateBuffer(); + void SwitchToMainBuffer(); + void Clear(); + void DisplayException(Exception ex); + void StandardOutput(string stdOut); + void StandardError(string stdErr); + void Info(FormattableString formattableString); + void Warning(FormattableString formattableString); + void Success(FormattableString formattableString); + void Error(FormattableString formattableString); + void WaitKey(); + void Line(); +} diff --git a/src/App/Abstractions/Icons.cs b/src/App/Abstractions/Icons.cs new file mode 100644 index 0000000..6deff37 --- /dev/null +++ b/src/App/Abstractions/Icons.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Debianet.Abstractions; + +internal static class Icons +{ + public const string Back = "←"; + public const string Forward = "→"; + public const string Up = "↑"; + public const string Down = "↓"; +} diff --git a/src/App/Abstractions/Palete.cs b/src/App/Abstractions/Palete.cs new file mode 100644 index 0000000..4fc7034 --- /dev/null +++ b/src/App/Abstractions/Palete.cs @@ -0,0 +1,21 @@ +using Spectre.Console; + +namespace Debianet.Abstractions; + +internal sealed class Palete +{ + public required Color Accent { get; init; } + public required Color Warning { get; init; } + public required Color Error { get; init; } + public required Color Info { get; init; } + public required Color Success { get; init; } + + public static readonly Palete Dracula = new() + { + Accent = Color.FromHex("#F8F8F2"), + Error = Color.FromHex("#FF5555"), + Warning = Color.FromHex("#F1FA8C"), + Info = Color.FromHex("#BD93F9"), + Success = Color.FromHex("#50FA7B") + }; +} diff --git a/src/App/Abstractions/Terminal.cs b/src/App/Abstractions/Terminal.cs new file mode 100644 index 0000000..d88b50e --- /dev/null +++ b/src/App/Abstractions/Terminal.cs @@ -0,0 +1,52 @@ +using Spectre.Console; + +namespace Debianet.Abstractions; + +internal sealed class Terminal : ITerminal +{ + private readonly Palete _palete; + + public Terminal(Palete? palete = null) + { + _palete = palete ?? Palete.Dracula; + } + + public void Clear() + => AnsiConsole.Clear(); + + public void DisplayException(Exception ex) + => AnsiConsole.WriteException(ex); + + public void Info(FormattableString formattableString) + => AnsiConsole.MarkupLine($"[#{_palete.Info.ToHex()}]{formattableString}[/]"); + + public void Warning(FormattableString formattableString) + => AnsiConsole.MarkupLine($"[#{_palete.Warning.ToHex()}]{formattableString}[/]"); + + public void Success(FormattableString formattableString) + => AnsiConsole.MarkupLine($"[#{_palete.Success.ToHex()}]{formattableString}[/]"); + + public void Error(FormattableString formattableString) + => AnsiConsole.MarkupLine($"[#{_palete.Error.ToHex()}]{formattableString}[/]"); + + public void StandardError(string stdErr) + => AnsiConsole.MarkupLine($"[#{_palete.Error.ToHex()}]{stdErr}[/]"); + + public void StandardOutput(string stdOut) + => AnsiConsole.WriteLine(stdOut); + + public void SwitchToAlternateBuffer() + => AnsiConsole.WriteLine("\e[?1049h"); + + public void SwitchToMainBuffer() + => AnsiConsole.WriteLine("\e[?1049l"); + + public void WaitKey() + { + AnsiConsole.MarkupLine($"[#{_palete.Accent.ToHex()}]Press any key to continue...[/]"); + Console.ReadKey(); + } + + public void Line() + => AnsiConsole.Write(new Rule()); +} \ No newline at end of file diff --git a/src/App/Debianet.csproj b/src/App/Debianet.csproj new file mode 100644 index 0000000..0a0b716 --- /dev/null +++ b/src/App/Debianet.csproj @@ -0,0 +1,34 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/src/App/Debianet.slnx b/src/App/Debianet.slnx new file mode 100644 index 0000000..b6b0814 --- /dev/null +++ b/src/App/Debianet.slnx @@ -0,0 +1,3 @@ + + + diff --git a/src/App/MenuRegistry.cs b/src/App/MenuRegistry.cs new file mode 100644 index 0000000..62d023b --- /dev/null +++ b/src/App/MenuRegistry.cs @@ -0,0 +1,16 @@ +using Debianet.Abstractions; +using Debianet.Menus; + +namespace Debianet; + +internal class MenuRegistry +{ + public MenuRegistry(ITerminal terminal) + { + Main = new MainMenu(this, terminal); + Dotnet = new DotnetMenu(this, terminal); + } + + public MainMenu Main { get; } + public DotnetMenu Dotnet { get; } +} diff --git a/src/App/Menus/DotnetMenu.cs b/src/App/Menus/DotnetMenu.cs new file mode 100644 index 0000000..2df2600 --- /dev/null +++ b/src/App/Menus/DotnetMenu.cs @@ -0,0 +1,81 @@ +using Debianet.Abstractions; +using Debianet.Properties; +using Debianet.Ui; + +using Spectre.Console; + +namespace Debianet.Menus; + +internal class DotnetMenu : Menu +{ + private readonly ITerminal _terminal; + + public DotnetMenu(MenuRegistry menuRegistry, ITerminal terminal) + : base(menuRegistry) + { + _terminal = terminal; + } + + public override string Title + => Resources.DotnetMenu_Title; + + public override void BeforeSelection() + { + AnsiConsole.Write(new FigletText(".NET")); + } + + public override IEnumerable Items + { + get + { + yield return new ReplaceMenuMenuItem + { + Icon = Icons.Back, + Text = Resources.DotnetMenu_Item_Back, + Submenu = MenuRegistry.Main + }; + yield return new RunCommandMenuItem(_terminal) + { + Text = Resources.DotnetMenu_Item_ListRuntimes, + Program = "dotnet", + Arguments = ["--list-runtimes"] + }; + yield return new RunCommandMenuItem(_terminal) + { + Text = Resources.DotnetMenu_Item_ListSdks, + Program = "dotnet", + Arguments = ["--list-sdks"] + }; + yield return new RunCommandMenuItem(_terminal) + { + Text = Resources.DotnetMenu_Item_ListWorkloads, + Program = "dotnet", + Arguments = ["workload", "list"] + }; + yield return new RunCommandMenuItem(_terminal) + { + Text = Resources.DotnetMenu_Item_ListTools, + Program = "dotnet", + Arguments = ["tool", "list", "-g"] + }; + yield return new RunCommandMenuItem(_terminal) + { + Text = Resources.DotnetMenu_Item_UpdateWorkloads, + Program = "dotnet", + Arguments = ["workload", "update"] + }; + yield return new RunCommandMenuItem(_terminal) + { + Text = Resources.DotnetMenu_Item_UpdateTools, + Program = "dotnet", + Arguments = ["tool", "update", "-g", "--all"] + }; + yield return new RunCommandMenuItem(_terminal) + { + Text = Resources.DotnetMenu_Item_ClearNugetCache, + Program = "dotnet", + Arguments = ["nuget", "locals", "all", "--clear"] + }; + } + } +} diff --git a/src/App/Menus/MainMenu.cs b/src/App/Menus/MainMenu.cs new file mode 100644 index 0000000..70d3558 --- /dev/null +++ b/src/App/Menus/MainMenu.cs @@ -0,0 +1,66 @@ +using Debianet.Abstractions; +using Debianet.Properties; +using Debianet.Ui; + +namespace Debianet.Menus; + +internal class MainMenu : Menu +{ + private readonly ITerminal _terminal; + + public MainMenu(MenuRegistry menuRegistry, ITerminal terminal) + : base(menuRegistry) + { + _terminal = terminal; + } + + public override string Title + => Resources.MainMenu_Title; + + public override void BeforeSelection() + { + const string text = """ + @ + @@@@@@@@@@@@@ + @@@@ @@@@ + @ @@ @@@ @@@@@ @@@ @@@@@@@@@@ @@@@@@@@@@@@@@ + %@ @@@@ @@ @@@@@@ @@@ @@@ @@@ + @@ @@ @@@ @@@ @@@ @@@ @@@ + @ @ @ @@@ @@@@ @@@ @@@ @@@ + @ @ @@ @@@ @@@@ @@@ @@@@@@@@@@ @@@ + @@ @@ @ @@@ @@@@ @@@ @@@ @@@ + @@ :@* @@@ @@@ @@@ @@@ @@@ + @@ @@@ @@@@@@ @@@ @@@ + @@ @@@ @@@@@ @@@@@@@@@@ @@@ + @ + @@ + @@ + """; + + _terminal.Info($"{text}"); + } + + public override IEnumerable Items + { + get + { + yield return new ReplaceMenuMenuItem + { + Icon = Icons.Forward, + Text = Resources.MainMenu_Item_Dotnet, + Submenu = MenuRegistry.Dotnet + }; + yield return new DelegateMenuItem() + { + Text = Resources.MainMenu_Item_Exit, + Action = Exit + }; + } + } + + private void Exit() + { + _terminal.Clear(); + Environment.Exit(0); + } +} diff --git a/src/App/Program.cs b/src/App/Program.cs new file mode 100644 index 0000000..02632ab --- /dev/null +++ b/src/App/Program.cs @@ -0,0 +1,8 @@ +using Debianet; +using Debianet.Abstractions; + +var terminal = new Terminal(); +var registry = new MenuRegistry(terminal); +var application = new Application(terminal, registry); + +await application.Run(); \ No newline at end of file diff --git a/src/App/Properties/Resources.Designer.cs b/src/App/Properties/Resources.Designer.cs new file mode 100644 index 0000000..878dbd0 --- /dev/null +++ b/src/App/Properties/Resources.Designer.cs @@ -0,0 +1,171 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Debianet.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Debianet.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Back to main menu. + /// + internal static string DotnetMenu_Item_Back { + get { + return ResourceManager.GetString("DotnetMenu_Item_Back", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Clear all local NuGet caches.... + /// + internal static string DotnetMenu_Item_ClearNugetCache { + get { + return ResourceManager.GetString("DotnetMenu_Item_ClearNugetCache", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List installed .NET runtimes.... + /// + internal static string DotnetMenu_Item_ListRuntimes { + get { + return ResourceManager.GetString("DotnetMenu_Item_ListRuntimes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List installed .NET SDKs.... + /// + internal static string DotnetMenu_Item_ListSdks { + get { + return ResourceManager.GetString("DotnetMenu_Item_ListSdks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List installed global tools.... + /// + internal static string DotnetMenu_Item_ListTools { + get { + return ResourceManager.GetString("DotnetMenu_Item_ListTools", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List installed .NET workloads.... + /// + internal static string DotnetMenu_Item_ListWorkloads { + get { + return ResourceManager.GetString("DotnetMenu_Item_ListWorkloads", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update installed global tools.... + /// + internal static string DotnetMenu_Item_UpdateTools { + get { + return ResourceManager.GetString("DotnetMenu_Item_UpdateTools", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update installed workloads.... + /// + internal static string DotnetMenu_Item_UpdateWorkloads { + get { + return ResourceManager.GetString("DotnetMenu_Item_UpdateWorkloads", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to .NET commands. + /// + internal static string DotnetMenu_Title { + get { + return ResourceManager.GetString("DotnetMenu_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to .NET related commands.... + /// + internal static string MainMenu_Item_Dotnet { + get { + return ResourceManager.GetString("MainMenu_Item_Dotnet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exit. + /// + internal static string MainMenu_Item_Exit { + get { + return ResourceManager.GetString("MainMenu_Item_Exit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Debianet main menu. + /// + internal static string MainMenu_Title { + get { + return ResourceManager.GetString("MainMenu_Title", resourceCulture); + } + } + } +} diff --git a/src/App/Properties/Resources.resx b/src/App/Properties/Resources.resx new file mode 100644 index 0000000..86d6bbe --- /dev/null +++ b/src/App/Properties/Resources.resx @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Back to main menu + + + Clear all local NuGet caches... + + + List installed .NET runtimes... + + + List installed .NET SDKs... + + + List installed global tools... + + + List installed .NET workloads... + + + Update installed global tools... + + + Update installed workloads... + + + .NET commands + + + .NET related commands... + + + Exit + + + Debianet main menu + + \ No newline at end of file diff --git a/src/App/Ui/DelegateMenuItem.cs b/src/App/Ui/DelegateMenuItem.cs new file mode 100644 index 0000000..2452a8f --- /dev/null +++ b/src/App/Ui/DelegateMenuItem.cs @@ -0,0 +1,14 @@ +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal sealed class DelegateMenuItem : MenuItemBase +{ + public required Action Action { get; init; } + + public override Task Execute(IApplication app, CancellationToken cancellationToken) + { + Action(); + return Task.CompletedTask; + } +} diff --git a/src/App/Ui/Menu.cs b/src/App/Ui/Menu.cs new file mode 100644 index 0000000..00832c2 --- /dev/null +++ b/src/App/Ui/Menu.cs @@ -0,0 +1,20 @@ +namespace Debianet.Ui; + +internal abstract class Menu +{ + public MenuRegistry MenuRegistry { get; } + + public Menu(MenuRegistry menuRegistry) + { + MenuRegistry = menuRegistry; + } + + public abstract string Title { get; } + + public abstract IEnumerable Items { get; } + + public virtual void BeforeSelection() + { + + } +} diff --git a/src/App/Ui/MenuItemBase.cs b/src/App/Ui/MenuItemBase.cs new file mode 100644 index 0000000..8895efb --- /dev/null +++ b/src/App/Ui/MenuItemBase.cs @@ -0,0 +1,10 @@ +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal abstract class MenuItemBase +{ + public string? Icon { get; init; } + public required string Text { get; init; } + public abstract Task Execute(IApplication app, CancellationToken cancellationToken); +} diff --git a/src/App/Ui/ReplaceMenuMenuItem.cs b/src/App/Ui/ReplaceMenuMenuItem.cs new file mode 100644 index 0000000..49aa6b4 --- /dev/null +++ b/src/App/Ui/ReplaceMenuMenuItem.cs @@ -0,0 +1,13 @@ +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal class ReplaceMenuMenuItem : MenuItemBase +{ + public required Menu Submenu { get; init; } + + public override async Task Execute(IApplication app, CancellationToken cancellationToken) + { + app.SwitchMenu(Submenu); + } +} diff --git a/src/App/Ui/RunCommandMenuItem.cs b/src/App/Ui/RunCommandMenuItem.cs new file mode 100644 index 0000000..d8794cc --- /dev/null +++ b/src/App/Ui/RunCommandMenuItem.cs @@ -0,0 +1,45 @@ +using CliWrap; + +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal sealed class RunCommandMenuItem(ITerminal terminal) : MenuItemBase +{ + public required string Program { get; init; } + + public required string[] Arguments { get; init; } + + public override async Task Execute(IApplication app, CancellationToken cancellationToken) + { + terminal.Info($"Executing {Program} {string.Join(' ', Arguments)} ..."); + terminal.Line(); + + var result = await Cli.Wrap(Program) + .WithArguments(Arguments) + .WithStandardOutputPipe(PipeTarget.ToDelegate(terminal.StandardOutput)) + .WithStandardErrorPipe(PipeTarget.ToDelegate(terminal.StandardError)) + .ExecuteAsync(cancellationToken); + + terminal.Line(); + terminal.Info($"Exit code: {result.ExitCode}, Run time: {FormatTime(result.RunTime)}"); + terminal.WaitKey(); + } + + private static string FormatTime(TimeSpan timeSpan) + { + if (timeSpan.TotalMilliseconds < 1000) + { + return $"{timeSpan.TotalMilliseconds:F0} ms"; + } + else if (timeSpan.TotalSeconds < 60) + { + return $"{timeSpan.TotalSeconds:F2} s"; + } + else if (timeSpan.TotalMinutes < 60) + { + return $"{(int)timeSpan.TotalMinutes} minutes {timeSpan.Seconds:D2} seconds"; + } + return $"{(int)timeSpan.TotalHours} hours {timeSpan.Minutes:D2} minutes {timeSpan.Seconds:D2} seconds"; + } +} diff --git a/src/branding/DebiaNet_2.png b/src/branding/DebiaNet_2.png new file mode 100644 index 0000000..370e807 --- /dev/null +++ b/src/branding/DebiaNet_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63a188a104e59016ec8a39538e44f5b0ac9091631203a92e96b2a0fdc276a8be +size 35866 diff --git a/src/branding/debianet2.svg b/src/branding/debianet2.svg new file mode 100644 index 0000000..41d5dab --- /dev/null +++ b/src/branding/debianet2.svg @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + NET + diff --git a/src/install.sh b/src/install.sh index 63dccfb..a8cc58e 100644 --- a/src/install.sh +++ b/src/install.sh @@ -87,6 +87,9 @@ wget https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep_15.1 sudo dpkg -i ripgrep.deb rm ripgrep.deb +# install Fresh +curl https://raw.githubusercontent.com/sinelaw/fresh/refs/heads/master/scripts/install.sh | sh + # install dive DIVE_VERSION=$(curl -sL "https://api.github.com/repos/wagoodman/dive/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/') curl -fOL "https://github.com/wagoodman/dive/releases/download/v${DIVE_VERSION}/dive_${DIVE_VERSION}_linux_amd64.deb" From bc6c0e8ea586e17f3a5bd789d95e1a93300b9f85 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Thu, 11 Jun 2026 20:10:48 +0200 Subject: [PATCH 07/14] .NET Tools installer menu --- src/App/Abstractions/Application.cs | 84 +++++++++++++++++------ src/App/MenuRegistry.cs | 2 + src/App/Menus/DotnetToolsInstallerMenu.cs | 80 +++++++++++++++++++++ src/App/Menus/MainMenu.cs | 6 ++ src/App/Properties/Resources.Designer.cs | 27 ++++++++ src/App/Properties/Resources.resx | 9 +++ src/App/Ui/Extensions.cs | 21 ++++++ src/App/Ui/MenuItem.cs | 11 +++ src/App/Ui/MultiSelectMenu.cs | 11 +++ src/App/Ui/RunCommandMenuItem.cs | 19 +---- src/install.sh | 20 ------ 11 files changed, 232 insertions(+), 58 deletions(-) create mode 100644 src/App/Menus/DotnetToolsInstallerMenu.cs create mode 100644 src/App/Ui/Extensions.cs create mode 100644 src/App/Ui/MenuItem.cs create mode 100644 src/App/Ui/MultiSelectMenu.cs diff --git a/src/App/Abstractions/Application.cs b/src/App/Abstractions/Application.cs index 0b56b9f..b0e5997 100644 --- a/src/App/Abstractions/Application.cs +++ b/src/App/Abstractions/Application.cs @@ -1,4 +1,5 @@ -using Debianet.Ui; +using Debianet.Properties; +using Debianet.Ui; using Spectre.Console; @@ -102,25 +103,10 @@ public async Task Run() try { _currentMenu.BeforeSelection(); - var prompt = new SelectionPrompt() - .Title(_currentMenu.Title) - .UseConverter(MenuitemConverter) - .AddChoices(_currentMenu.Items); - - var grid = new Grid(); - grid.AddColumn(); - grid.AddColumn(); - - - prompt.SearchEnabled = true; - var selection = AnsiConsole.Prompt(prompt); - - using (var tokenSource = new ConsoleCancellationTokenSource()) - { - _terminal.SwitchToMainBuffer(); - _terminal.Clear(); - await selection.Execute(this, tokenSource.Token); - } + if (_currentMenu is MultiSelectMenu multiSelectMenu) + await DoMultiSelectionMenu(multiSelectMenu); + else + await DoSingleSelectionMenu(_currentMenu); } catch (Exception ex) { @@ -129,6 +115,64 @@ public async Task Run() } } + private async Task DoMultiSelectionMenu(MultiSelectMenu menu) + { + static List OnCancel() + => []; + + var prompt = new MultiSelectionPrompt() + .Title(menu.Title) + .UseConverter(MenuitemConverter) + .AddCancelResult(OnCancel) + .PageSize(GetPageSize()) + .InstructionsText(Resources.App_MultiSelectionInstructions) + .AddChoices(menu.Items); + + List selections = AnsiConsole.Prompt(prompt); + + using (var tokenSource = new ConsoleCancellationTokenSource()) + { + _terminal.SwitchToMainBuffer(); + _terminal.Clear(); + await menu.ProcessSelectedItems(selections, tokenSource.Token); + } + } + + private async Task DoSingleSelectionMenu(Menu menu) + { + static MenuItemBase OnCancel() + { + return new DelegateMenuItem() + { + Action = () => { }, + Text = "Cancel" + }; + } + + var prompt = new SelectionPrompt() + .Title(menu.Title) + .UseConverter(MenuitemConverter) + .AddCancelResult(OnCancel) + .PageSize(GetPageSize()) + .AddChoices(menu.Items); + + prompt.SearchEnabled = true; + var selection = AnsiConsole.Prompt(prompt); + + using (var tokenSource = new ConsoleCancellationTokenSource()) + { + _terminal.SwitchToMainBuffer(); + _terminal.Clear(); + await selection.Execute(this, tokenSource.Token); + } + } + + private static int GetPageSize() + { + int size = Console.WindowHeight - Console.CursorTop - 2; + return size < 1 ? 1 : size; + } + private string MenuitemConverter(MenuItemBase item) { if (item.Icon != null) diff --git a/src/App/MenuRegistry.cs b/src/App/MenuRegistry.cs index 62d023b..3fa8502 100644 --- a/src/App/MenuRegistry.cs +++ b/src/App/MenuRegistry.cs @@ -9,8 +9,10 @@ public MenuRegistry(ITerminal terminal) { Main = new MainMenu(this, terminal); Dotnet = new DotnetMenu(this, terminal); + DotnetToolInstaller = new DotnetToolsInstallerMenu(this, terminal); } public MainMenu Main { get; } public DotnetMenu Dotnet { get; } + public DotnetToolsInstallerMenu DotnetToolInstaller { get; } } diff --git a/src/App/Menus/DotnetToolsInstallerMenu.cs b/src/App/Menus/DotnetToolsInstallerMenu.cs new file mode 100644 index 0000000..36706a8 --- /dev/null +++ b/src/App/Menus/DotnetToolsInstallerMenu.cs @@ -0,0 +1,80 @@ +using CliWrap; + +using Debianet.Abstractions; +using Debianet.Properties; +using Debianet.Ui; + +namespace Debianet.Menus; + +internal class DotnetToolsInstallerMenu : MultiSelectMenu +{ + private readonly ITerminal _terminal; + + public DotnetToolsInstallerMenu(MenuRegistry menuRegistry, ITerminal terminal) + : base(menuRegistry) + { + _terminal = terminal; + } + + public override string Title + => Resources.DotnetToolsInstall_Title; + + private MenuItem CreateToolInstaller(string toolName) + { + return new MenuItem + { + Text = toolName, + Data = toolName + }; + } + + public override IEnumerable Items + { + get + { + yield return CreateToolInstaller("dotnet-counters"); + yield return CreateToolInstaller("dotnet-coverage"); + yield return CreateToolInstaller("dotnet-ef"); + yield return CreateToolInstaller("dotnet-counters"); + yield return CreateToolInstaller("dotnet-gcdump"); + yield return CreateToolInstaller("dotnet-monitor"); + yield return CreateToolInstaller("dotnet-stack"); + yield return CreateToolInstaller("dotnet-symbol"); + yield return CreateToolInstaller("dotnet-trace"); + yield return CreateToolInstaller("Microsoft.VisualStudio.SlnGen.Tool"); + yield return CreateToolInstaller("PowerShell"); + yield return CreateToolInstaller("upgrade-assistant"); + yield return CreateToolInstaller("docfx"); + yield return CreateToolInstaller("csharprepl"); + yield return CreateToolInstaller("ilspycmd"); + yield return CreateToolInstaller("roslynator.dotnet.cli"); + yield return CreateToolInstaller("CsProj"); + } + } + + public override async Task ProcessSelectedItems(IReadOnlyList selectedItems, CancellationToken cancellationToken) + { + int counter = 0; + var items = selectedItems.OfType().ToArray(); + TimeSpan totalTime = TimeSpan.Zero; + + foreach (var item in items) + { + _terminal.Info($"{counter}/{items.Length}: Installing {item.Data}..."); + _terminal.Line(); + + var result = await Cli.Wrap("dotnet") + .WithArguments(["tool", "install", "-g", item.Data]) + .WithStandardOutputPipe(PipeTarget.ToDelegate(_terminal.StandardOutput)) + .WithStandardErrorPipe(PipeTarget.ToDelegate(_terminal.StandardError)) + .ExecuteAsync(cancellationToken); + + _terminal.Line(); + ++counter; + totalTime += result.RunTime; + } + _terminal.Info($"Run time: {totalTime.FormatTime()}"); + _terminal.WaitKey(); + + } +} diff --git a/src/App/Menus/MainMenu.cs b/src/App/Menus/MainMenu.cs index 70d3558..ee82d80 100644 --- a/src/App/Menus/MainMenu.cs +++ b/src/App/Menus/MainMenu.cs @@ -50,6 +50,12 @@ public override IEnumerable Items Text = Resources.MainMenu_Item_Dotnet, Submenu = MenuRegistry.Dotnet }; + yield return new ReplaceMenuMenuItem + { + Icon = Icons.Forward, + Text = Resources.MainMenu_DotnetToolsInstall, + Submenu = MenuRegistry.DotnetToolInstaller + }; yield return new DelegateMenuItem() { Text = Resources.MainMenu_Item_Exit, diff --git a/src/App/Properties/Resources.Designer.cs b/src/App/Properties/Resources.Designer.cs index 878dbd0..33535b4 100644 --- a/src/App/Properties/Resources.Designer.cs +++ b/src/App/Properties/Resources.Designer.cs @@ -60,6 +60,15 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to (Press <space> to select, <enter> to accept, <esc> to cancel). + /// + internal static string App_MultiSelectionInstructions { + get { + return ResourceManager.GetString("App_MultiSelectionInstructions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Back to main menu. /// @@ -141,6 +150,24 @@ internal static string DotnetMenu_Title { } } + /// + /// Looks up a localized string similar to .NET tool installer. + /// + internal static string DotnetToolsInstall_Title { + get { + return ResourceManager.GetString("DotnetToolsInstall_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Install .NET Tools.... + /// + internal static string MainMenu_DotnetToolsInstall { + get { + return ResourceManager.GetString("MainMenu_DotnetToolsInstall", resourceCulture); + } + } + /// /// Looks up a localized string similar to .NET related commands.... /// diff --git a/src/App/Properties/Resources.resx b/src/App/Properties/Resources.resx index 86d6bbe..1dcaf56 100644 --- a/src/App/Properties/Resources.resx +++ b/src/App/Properties/Resources.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + (Press <space> to select, <enter> to accept, <esc> to cancel) + Back to main menu @@ -144,6 +147,12 @@ .NET commands + + .NET tool installer + + + Install .NET Tools... + .NET related commands... diff --git a/src/App/Ui/Extensions.cs b/src/App/Ui/Extensions.cs new file mode 100644 index 0000000..a731754 --- /dev/null +++ b/src/App/Ui/Extensions.cs @@ -0,0 +1,21 @@ +namespace Debianet.Ui; + +internal static class Extensions +{ + public static string FormatTime(this TimeSpan timeSpan) + { + if (timeSpan.TotalMilliseconds < 1000) + { + return $"{timeSpan.TotalMilliseconds:F0} ms"; + } + else if (timeSpan.TotalSeconds < 60) + { + return $"{timeSpan.TotalSeconds:F2} s"; + } + else if (timeSpan.TotalMinutes < 60) + { + return $"{(int)timeSpan.TotalMinutes} minutes {timeSpan.Seconds:D2} seconds"; + } + return $"{(int)timeSpan.TotalHours} hours {timeSpan.Minutes:D2} minutes {timeSpan.Seconds:D2} seconds"; + } +} diff --git a/src/App/Ui/MenuItem.cs b/src/App/Ui/MenuItem.cs new file mode 100644 index 0000000..7d0235a --- /dev/null +++ b/src/App/Ui/MenuItem.cs @@ -0,0 +1,11 @@ +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal class MenuItem : MenuItemBase +{ + public string Data { get; set; } = ""; + + public override Task Execute(IApplication app, CancellationToken cancellationToken) + => Task.CompletedTask; +} \ No newline at end of file diff --git a/src/App/Ui/MultiSelectMenu.cs b/src/App/Ui/MultiSelectMenu.cs new file mode 100644 index 0000000..ecee726 --- /dev/null +++ b/src/App/Ui/MultiSelectMenu.cs @@ -0,0 +1,11 @@ +namespace Debianet.Ui; + +internal abstract class MultiSelectMenu : Menu +{ + protected MultiSelectMenu(MenuRegistry menuRegistry) + : base(menuRegistry) + { + } + + public abstract Task ProcessSelectedItems(IReadOnlyList selectedItems, CancellationToken cancellationToken); +} diff --git a/src/App/Ui/RunCommandMenuItem.cs b/src/App/Ui/RunCommandMenuItem.cs index d8794cc..81e3f3d 100644 --- a/src/App/Ui/RunCommandMenuItem.cs +++ b/src/App/Ui/RunCommandMenuItem.cs @@ -22,24 +22,7 @@ public override async Task Execute(IApplication app, CancellationToken cancellat .ExecuteAsync(cancellationToken); terminal.Line(); - terminal.Info($"Exit code: {result.ExitCode}, Run time: {FormatTime(result.RunTime)}"); + terminal.Info($"Exit code: {result.ExitCode}, Run time: {result.RunTime.FormatTime()}"); terminal.WaitKey(); } - - private static string FormatTime(TimeSpan timeSpan) - { - if (timeSpan.TotalMilliseconds < 1000) - { - return $"{timeSpan.TotalMilliseconds:F0} ms"; - } - else if (timeSpan.TotalSeconds < 60) - { - return $"{timeSpan.TotalSeconds:F2} s"; - } - else if (timeSpan.TotalMinutes < 60) - { - return $"{(int)timeSpan.TotalMinutes} minutes {timeSpan.Seconds:D2} seconds"; - } - return $"{(int)timeSpan.TotalHours} hours {timeSpan.Minutes:D2} minutes {timeSpan.Seconds:D2} seconds"; - } } diff --git a/src/install.sh b/src/install.sh index a8cc58e..15cb4db 100644 --- a/src/install.sh +++ b/src/install.sh @@ -99,26 +99,6 @@ rm ./dive_${DIVE_VERSION}_linux_amd64.deb # update dotnet workloads sudo dotnet workload update -# install .NET Global tools -dotnet tool install --global dotnet-counters -dotnet tool install --global dotnet-coverage -dotnet tool install --global dotnet-ef -dotnet tool install --global dotnet-gcdump -dotnet tool install --global dotnet-monitor -dotnet tool install --global dotnet-stack -dotnet tool install --global dotnet-symbol -dotnet tool install --global dotnet-trace -dotnet tool install --global Microsoft.VisualStudio.SlnGen.Tool -dotnet tool install --global PowerShell -dotnet tool install --global upgrade-assistant -dotnet tool install --global docfx - -# install third party dotnet tools -dotnet tool install --global csharprepl -dotnet tool install --global ilspycmd -dotnet tool install --global roslynator.dotnet.cli -dotnet tool install --global CsProj - # set path for dotnet tools echo 'export PATH="$PATH:$HOME/.dotnet/tools"' >> ~/.bashrc echo 'eval "$(dotnet completions script bash)"' >> ~/.bashrc From 9b77b789b5afe52c96f325c36d8f4c5961cd6299 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sat, 13 Jun 2026 15:30:17 +0200 Subject: [PATCH 08/14] Icons and .NET tool installer menu finished --- src/App/Abstractions/ITerminal.cs | 1 + src/App/Abstractions/Icons.cs | 17 ++++++++--------- src/App/Abstractions/Terminal.cs | 10 ++++++++++ src/App/Menus/DotnetMenu.cs | 11 ++++++++--- src/App/Menus/DotnetToolsInstallerMenu.cs | 11 ++++++++++- src/App/Menus/MainMenu.cs | 5 +++-- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/App/Abstractions/ITerminal.cs b/src/App/Abstractions/ITerminal.cs index e92720f..7d1a046 100644 --- a/src/App/Abstractions/ITerminal.cs +++ b/src/App/Abstractions/ITerminal.cs @@ -8,6 +8,7 @@ internal interface ITerminal void DisplayException(Exception ex); void StandardOutput(string stdOut); void StandardError(string stdErr); + void FigletText(string text); void Info(FormattableString formattableString); void Warning(FormattableString formattableString); void Success(FormattableString formattableString); diff --git a/src/App/Abstractions/Icons.cs b/src/App/Abstractions/Icons.cs index 6deff37..f7d1dc0 100644 --- a/src/App/Abstractions/Icons.cs +++ b/src/App/Abstractions/Icons.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Debianet.Abstractions; +namespace Debianet.Abstractions; internal static class Icons { - public const string Back = "←"; - public const string Forward = "→"; - public const string Up = "↑"; - public const string Down = "↓"; + public const string SubMenu = "↪️"; + public const string Back = "🔙"; + public const string Package = "📦"; + public const string Door = "🚪"; + public const string Info = "ℹ️"; + public const string Refresh = "🔄"; + public const string Trash = "🗑️"; } diff --git a/src/App/Abstractions/Terminal.cs b/src/App/Abstractions/Terminal.cs index d88b50e..e67181b 100644 --- a/src/App/Abstractions/Terminal.cs +++ b/src/App/Abstractions/Terminal.cs @@ -49,4 +49,14 @@ public void WaitKey() public void Line() => AnsiConsole.Write(new Rule()); + + public void FigletText(string text) + { + var figlet = new FigletText(text) + .Color(_palete.Info) + .Justify(Justify.Left); + + AnsiConsole.Write(figlet); + } + } \ No newline at end of file diff --git a/src/App/Menus/DotnetMenu.cs b/src/App/Menus/DotnetMenu.cs index 2df2600..e2622ae 100644 --- a/src/App/Menus/DotnetMenu.cs +++ b/src/App/Menus/DotnetMenu.cs @@ -20,9 +20,7 @@ public override string Title => Resources.DotnetMenu_Title; public override void BeforeSelection() - { - AnsiConsole.Write(new FigletText(".NET")); - } + => _terminal.FigletText(".NET"); public override IEnumerable Items { @@ -36,42 +34,49 @@ public override IEnumerable Items }; yield return new RunCommandMenuItem(_terminal) { + Icon = Icons.Info, Text = Resources.DotnetMenu_Item_ListRuntimes, Program = "dotnet", Arguments = ["--list-runtimes"] }; yield return new RunCommandMenuItem(_terminal) { + Icon = Icons.Info, Text = Resources.DotnetMenu_Item_ListSdks, Program = "dotnet", Arguments = ["--list-sdks"] }; yield return new RunCommandMenuItem(_terminal) { + Icon = Icons.Info, Text = Resources.DotnetMenu_Item_ListWorkloads, Program = "dotnet", Arguments = ["workload", "list"] }; yield return new RunCommandMenuItem(_terminal) { + Icon = Icons.Info, Text = Resources.DotnetMenu_Item_ListTools, Program = "dotnet", Arguments = ["tool", "list", "-g"] }; yield return new RunCommandMenuItem(_terminal) { + Icon = Icons.Refresh, Text = Resources.DotnetMenu_Item_UpdateWorkloads, Program = "dotnet", Arguments = ["workload", "update"] }; yield return new RunCommandMenuItem(_terminal) { + Icon = Icons.Refresh, Text = Resources.DotnetMenu_Item_UpdateTools, Program = "dotnet", Arguments = ["tool", "update", "-g", "--all"] }; yield return new RunCommandMenuItem(_terminal) { + Icon = Icons.Trash, Text = Resources.DotnetMenu_Item_ClearNugetCache, Program = "dotnet", Arguments = ["nuget", "locals", "all", "--clear"] diff --git a/src/App/Menus/DotnetToolsInstallerMenu.cs b/src/App/Menus/DotnetToolsInstallerMenu.cs index 36706a8..1d53dd3 100644 --- a/src/App/Menus/DotnetToolsInstallerMenu.cs +++ b/src/App/Menus/DotnetToolsInstallerMenu.cs @@ -4,6 +4,8 @@ using Debianet.Properties; using Debianet.Ui; +using Spectre.Console; + namespace Debianet.Menus; internal class DotnetToolsInstallerMenu : MultiSelectMenu @@ -24,10 +26,14 @@ private MenuItem CreateToolInstaller(string toolName) return new MenuItem { Text = toolName, - Data = toolName + Data = toolName, + Icon = Icons.Package }; } + public override void BeforeSelection() + => _terminal.FigletText(".NET tools"); + public override IEnumerable Items { get @@ -54,6 +60,9 @@ public override IEnumerable Items public override async Task ProcessSelectedItems(IReadOnlyList selectedItems, CancellationToken cancellationToken) { + if (selectedItems.Count == 0) + return; + int counter = 0; var items = selectedItems.OfType().ToArray(); TimeSpan totalTime = TimeSpan.Zero; diff --git a/src/App/Menus/MainMenu.cs b/src/App/Menus/MainMenu.cs index ee82d80..e6ec5b3 100644 --- a/src/App/Menus/MainMenu.cs +++ b/src/App/Menus/MainMenu.cs @@ -46,19 +46,20 @@ public override IEnumerable Items { yield return new ReplaceMenuMenuItem { - Icon = Icons.Forward, + Icon = Icons.SubMenu, Text = Resources.MainMenu_Item_Dotnet, Submenu = MenuRegistry.Dotnet }; yield return new ReplaceMenuMenuItem { - Icon = Icons.Forward, + Icon = Icons.SubMenu, Text = Resources.MainMenu_DotnetToolsInstall, Submenu = MenuRegistry.DotnetToolInstaller }; yield return new DelegateMenuItem() { Text = Resources.MainMenu_Item_Exit, + Icon = Icons.Door, Action = Exit }; } From bfed73a0b3848274b6def71b0109306a7206731c Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sat, 13 Jun 2026 16:00:42 +0200 Subject: [PATCH 09/14] Startup checks --- src/App/Abstractions/ITerminal.cs | 5 +- src/App/Abstractions/Terminal.cs | 24 +++++++++- src/App/Debianet.csproj | 1 + src/App/ExitCodes.cs | 8 ++++ src/App/Program.cs | 11 ++++- src/App/Properties/Resources.Designer.cs | 27 +++++++++++ src/App/Properties/Resources.resx | 9 ++++ src/App/StartupChecks.cs | 61 ++++++++++++++++++++++++ src/App/Ui/MessageBox.cs | 9 ++++ 9 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 src/App/ExitCodes.cs create mode 100644 src/App/StartupChecks.cs create mode 100644 src/App/Ui/MessageBox.cs diff --git a/src/App/Abstractions/ITerminal.cs b/src/App/Abstractions/ITerminal.cs index 7d1a046..47a0119 100644 --- a/src/App/Abstractions/ITerminal.cs +++ b/src/App/Abstractions/ITerminal.cs @@ -1,4 +1,6 @@ -namespace Debianet.Abstractions; +using Debianet.Ui; + +namespace Debianet.Abstractions; internal interface ITerminal { @@ -15,4 +17,5 @@ internal interface ITerminal void Error(FormattableString formattableString); void WaitKey(); void Line(); + void ShowMessageBox(MessageBox messageBox); } diff --git a/src/App/Abstractions/Terminal.cs b/src/App/Abstractions/Terminal.cs index e67181b..25bef2e 100644 --- a/src/App/Abstractions/Terminal.cs +++ b/src/App/Abstractions/Terminal.cs @@ -1,4 +1,6 @@ -using Spectre.Console; +using Debianet.Ui; + +using Spectre.Console; namespace Debianet.Abstractions; @@ -59,4 +61,24 @@ public void FigletText(string text) AnsiConsole.Write(figlet); } + public void ShowMessageBox(MessageBox messageBox) + { + SwitchToAlternateBuffer(); + Clear(); + + int topPad = (Console.WindowHeight / 2) - 4; + + Console.SetCursorPosition(0, topPad); + + var panel = new Panel(messageBox.Message) + .Header($"| {messageBox.Title} |", Justify.Center) + .HeavyBorder() + .BorderColor(_palete.Accent) + .Expand(); + + AnsiConsole.Write(panel); + + WaitKey(); + SwitchToMainBuffer(); + } } \ No newline at end of file diff --git a/src/App/Debianet.csproj b/src/App/Debianet.csproj index 0a0b716..dc519cb 100644 --- a/src/App/Debianet.csproj +++ b/src/App/Debianet.csproj @@ -5,6 +5,7 @@ net10.0 enable enable + true diff --git a/src/App/ExitCodes.cs b/src/App/ExitCodes.cs new file mode 100644 index 0000000..fdb0853 --- /dev/null +++ b/src/App/ExitCodes.cs @@ -0,0 +1,8 @@ +namespace Debianet; + +internal static class ExitCodes +{ + public const int NotLinux = ushort.MaxValue; + public const int SudoUser = 1; + public const int Success = 0; +} \ No newline at end of file diff --git a/src/App/Program.cs b/src/App/Program.cs index 02632ab..f86a25f 100644 --- a/src/App/Program.cs +++ b/src/App/Program.cs @@ -2,7 +2,16 @@ using Debianet.Abstractions; var terminal = new Terminal(); + +var startupChecks = new StartupChecks(terminal); +if (startupChecks.TryCheckExit(out int exitCode)) +{ + return exitCode; +} + var registry = new MenuRegistry(terminal); var application = new Application(terminal, registry); -await application.Run(); \ No newline at end of file +await application.Run(); + +return ExitCodes.Success; \ No newline at end of file diff --git a/src/App/Properties/Resources.Designer.cs b/src/App/Properties/Resources.Designer.cs index 33535b4..e40f35d 100644 --- a/src/App/Properties/Resources.Designer.cs +++ b/src/App/Properties/Resources.Designer.cs @@ -194,5 +194,32 @@ internal static string MainMenu_Title { return ResourceManager.GetString("MainMenu_Title", resourceCulture); } } + + /// + /// Looks up a localized string similar to Error. + /// + internal static string MessageBox_Error { + get { + return ResourceManager.GetString("MessageBox_Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This application is only supported on Linux systems.. + /// + internal static string StartupCheck_NotLinux { + get { + return ResourceManager.GetString("StartupCheck_NotLinux", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please run the application as a normal user.. + /// + internal static string StartupCheck_RootUser { + get { + return ResourceManager.GetString("StartupCheck_RootUser", resourceCulture); + } + } } } diff --git a/src/App/Properties/Resources.resx b/src/App/Properties/Resources.resx index 1dcaf56..7e92222 100644 --- a/src/App/Properties/Resources.resx +++ b/src/App/Properties/Resources.resx @@ -162,4 +162,13 @@ Debianet main menu + + Error + + + This application is only supported on Linux systems. + + + Please run the application as a normal user. + \ No newline at end of file diff --git a/src/App/StartupChecks.cs b/src/App/StartupChecks.cs new file mode 100644 index 0000000..13e426a --- /dev/null +++ b/src/App/StartupChecks.cs @@ -0,0 +1,61 @@ +using System.Runtime.InteropServices; + +using Debianet.Abstractions; +using Debianet.Properties; +using Debianet.Ui; + +namespace Debianet; + +internal sealed partial class StartupChecks +{ + private readonly Terminal _terminal; + + public StartupChecks(Terminal terminal) + { + _terminal = terminal; + } + + [LibraryImport("libc", EntryPoint = "geteuid")] + private static partial uint GetEUid(); + + private static bool IsRunningWithElevatedPriviliges() + { + try + { + uint euid = GetEUid(); + return euid == 0; + } + catch + { + return string.Equals(Environment.UserName, "root", StringComparison.Ordinal); + } + } + + public bool TryCheckExit(out int exitCode) + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + _terminal.ShowMessageBox(new MessageBox + { + Title = Resources.MessageBox_Error, + Message = Resources.StartupCheck_NotLinux + }); + exitCode = ExitCodes.NotLinux; + return true; + } + + if (IsRunningWithElevatedPriviliges()) + { + _terminal.ShowMessageBox(new MessageBox + { + Title = Resources.MessageBox_Error, + Message = Resources.StartupCheck_RootUser + }); + exitCode = ExitCodes.SudoUser; + return true; + } + + exitCode = ExitCodes.Success; + return false; + } +} diff --git a/src/App/Ui/MessageBox.cs b/src/App/Ui/MessageBox.cs new file mode 100644 index 0000000..7de18f0 --- /dev/null +++ b/src/App/Ui/MessageBox.cs @@ -0,0 +1,9 @@ +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal sealed class MessageBox +{ + public required string Title { get; set; } + public required string Message { get; set; } +} From 210e6938b29eca6d2b7cc94b0cfe6ea86915e824 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sun, 21 Jun 2026 16:04:14 +0200 Subject: [PATCH 10/14] Version and changelog added --- changelog.md | 4 + src/.gitignore | 484 +++++++++++++++++++++++ src/App/Abstractions/Icons.cs | 1 + src/App/AppVersionProvider.cs | 12 + src/App/Debianet.csproj | 18 +- src/App/Debianet.slnx | 4 + src/App/GlobalArgHandler.cs | 27 ++ src/App/Menus/MainMenu.cs | 18 +- src/App/Program.cs | 12 +- src/App/Properties/Resources.Designer.cs | 18 + src/App/Properties/Resources.resx | 6 + src/App/ResourceHandler.cs | 22 ++ src/App/Ui/MenuItemBase.cs | 1 + src/App/Ui/MessageBox.cs | 4 +- src/App/Ui/TextViewer.cs | 238 +++++++++++ 15 files changed, 852 insertions(+), 17 deletions(-) create mode 100644 src/.gitignore create mode 100644 src/App/AppVersionProvider.cs create mode 100644 src/App/GlobalArgHandler.cs create mode 100644 src/App/ResourceHandler.cs create mode 100644 src/App/Ui/TextViewer.cs diff --git a/changelog.md b/changelog.md index 57936ab..1813045 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 2026.06.21 +- New DebiaNet app +- Removed .NET tools from base install, can be installed via the DebiaNet app + ## 2026.04.04 - Version 26.04 - Based on Debian - Added byobu to default install diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..3b15c69 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e +# but not directories ending in .e2e +!*.e2e/ + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea/ + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/src/App/Abstractions/Icons.cs b/src/App/Abstractions/Icons.cs index f7d1dc0..903a936 100644 --- a/src/App/Abstractions/Icons.cs +++ b/src/App/Abstractions/Icons.cs @@ -9,4 +9,5 @@ internal static class Icons public const string Info = "ℹ️"; public const string Refresh = "🔄"; public const string Trash = "🗑️"; + public const string Text = "📄"; } diff --git a/src/App/AppVersionProvider.cs b/src/App/AppVersionProvider.cs new file mode 100644 index 0000000..e9a10d2 --- /dev/null +++ b/src/App/AppVersionProvider.cs @@ -0,0 +1,12 @@ +namespace Debianet; + +internal static class AppVersionProvider +{ + public static Version GetAppVersion() + { + Version? version = typeof(AppVersionProvider) + .Assembly.GetName().Version; + + return version ?? new Version(0, 0, 0, 0); + } +} diff --git a/src/App/Debianet.csproj b/src/App/Debianet.csproj index dc519cb..30ee531 100644 --- a/src/App/Debianet.csproj +++ b/src/App/Debianet.csproj @@ -6,8 +6,15 @@ enable enable true + $([System.DateTime]::UtcNow.ToString("yyyy")).$([System.DateTime]::UtcNow.ToString("MM")).$([System.DateTime]::UtcNow.ToString("dd")).0 + false + ..\bin\$(Configuration)\ + + + + @@ -18,6 +25,10 @@ + + ResXFileCodeGenerator + Resources.Designer.cs + True True @@ -25,11 +36,4 @@ - - - ResXFileCodeGenerator - Resources.Designer.cs - - - diff --git a/src/App/Debianet.slnx b/src/App/Debianet.slnx index b6b0814..a76b2dc 100644 --- a/src/App/Debianet.slnx +++ b/src/App/Debianet.slnx @@ -1,3 +1,7 @@ + + + + diff --git a/src/App/GlobalArgHandler.cs b/src/App/GlobalArgHandler.cs new file mode 100644 index 0000000..7448dae --- /dev/null +++ b/src/App/GlobalArgHandler.cs @@ -0,0 +1,27 @@ +using Spectre.Console; + +namespace Debianet; + +internal static class GlobalArgHandler +{ + public static void HandleGlobalArgs(string[] args) + { + HashSet argsSet = new HashSet(args, StringComparer.InvariantCultureIgnoreCase); + + if (argsSet.Contains("--wd") || argsSet.Contains("--wait-debugger")) + { + AnsiConsole.WriteLine("Waiting for debugger to attach..."); + while (!System.Diagnostics.Debugger.IsAttached) + { + Thread.Sleep(100); + } + AnsiConsole.WriteLine("Debugger attached."); + } + + if (argsSet.Contains("--version") || argsSet.Contains("-v")) + { + AnsiConsole.WriteLine(AppVersionProvider.GetAppVersion().ToString()); + Environment.Exit(0); + } + } +} diff --git a/src/App/Menus/MainMenu.cs b/src/App/Menus/MainMenu.cs index e6ec5b3..0c05559 100644 --- a/src/App/Menus/MainMenu.cs +++ b/src/App/Menus/MainMenu.cs @@ -19,7 +19,7 @@ public override string Title public override void BeforeSelection() { - const string text = """ + string text = $$""" @ @@@@@@@@@@@@@ @@@@ @@@@ @@ -34,7 +34,8 @@ @@ @@@ @@@@@@ @@@ @@@ @@ @@@ @@@@@ @@@@@@@@@@ @@@ @ @@ - @@ + @@ App Version: {{AppVersionProvider.GetAppVersion()}} + """; _terminal.Info($"{text}"); @@ -56,6 +57,12 @@ public override IEnumerable Items Text = Resources.MainMenu_DotnetToolsInstall, Submenu = MenuRegistry.DotnetToolInstaller }; + yield return new DelegateMenuItem + { + Text = Resources.MainMenu_Changelog, + Icon = Icons.Text, + Action = DisplayChangeLog + }; yield return new DelegateMenuItem() { Text = Resources.MainMenu_Item_Exit, @@ -65,6 +72,13 @@ public override IEnumerable Items } } + private void DisplayChangeLog() + { + var changelog = ResourceHandler.GetChangeLog(); + var viewer = new TextViewer(changelog, Resources.TextView_Changelog); + viewer.Show(); + } + private void Exit() { _terminal.Clear(); diff --git a/src/App/Program.cs b/src/App/Program.cs index f86a25f..784d3eb 100644 --- a/src/App/Program.cs +++ b/src/App/Program.cs @@ -3,11 +3,13 @@ var terminal = new Terminal(); -var startupChecks = new StartupChecks(terminal); -if (startupChecks.TryCheckExit(out int exitCode)) -{ - return exitCode; -} +GlobalArgHandler.HandleGlobalArgs(args); + +//var startupChecks = new StartupChecks(terminal); +//if (startupChecks.TryCheckExit(out int exitCode)) +//{ +// return exitCode; +//} var registry = new MenuRegistry(terminal); var application = new Application(terminal, registry); diff --git a/src/App/Properties/Resources.Designer.cs b/src/App/Properties/Resources.Designer.cs index e40f35d..60b57cf 100644 --- a/src/App/Properties/Resources.Designer.cs +++ b/src/App/Properties/Resources.Designer.cs @@ -159,6 +159,15 @@ internal static string DotnetToolsInstall_Title { } } + /// + /// Looks up a localized string similar to View Changelog.... + /// + internal static string MainMenu_Changelog { + get { + return ResourceManager.GetString("MainMenu_Changelog", resourceCulture); + } + } + /// /// Looks up a localized string similar to Install .NET Tools.... /// @@ -221,5 +230,14 @@ internal static string StartupCheck_RootUser { return ResourceManager.GetString("StartupCheck_RootUser", resourceCulture); } } + + /// + /// Looks up a localized string similar to Changelog. + /// + internal static string TextView_Changelog { + get { + return ResourceManager.GetString("TextView_Changelog", resourceCulture); + } + } } } diff --git a/src/App/Properties/Resources.resx b/src/App/Properties/Resources.resx index 7e92222..9fbf5b4 100644 --- a/src/App/Properties/Resources.resx +++ b/src/App/Properties/Resources.resx @@ -150,6 +150,9 @@ .NET tool installer + + View Changelog... + Install .NET Tools... @@ -171,4 +174,7 @@ Please run the application as a normal user. + + Changelog + \ No newline at end of file diff --git a/src/App/ResourceHandler.cs b/src/App/ResourceHandler.cs new file mode 100644 index 0000000..3f7d468 --- /dev/null +++ b/src/App/ResourceHandler.cs @@ -0,0 +1,22 @@ +namespace Debianet; + +internal static class ResourceHandler +{ + private static StreamReader GetEmbeddedStream(string resourceName) + { + return new StreamReader(typeof(ResourceHandler).Assembly.GetManifestResourceStream(resourceName) + ?? throw new InvalidOperationException($"Resource '{resourceName}' not found.")); + } + + public static string GetChangeLog() + { + using var reader = GetEmbeddedStream("Debianet.Properties.changelog.md"); + return reader.ReadToEnd(); + } + + public static string GetReadme() + { + using var reader = GetEmbeddedStream("Debianet.Properties.readme.md"); + return reader.ReadToEnd(); + } +} diff --git a/src/App/Ui/MenuItemBase.cs b/src/App/Ui/MenuItemBase.cs index 8895efb..d1aef25 100644 --- a/src/App/Ui/MenuItemBase.cs +++ b/src/App/Ui/MenuItemBase.cs @@ -7,4 +7,5 @@ internal abstract class MenuItemBase public string? Icon { get; init; } public required string Text { get; init; } public abstract Task Execute(IApplication app, CancellationToken cancellationToken); + public virtual bool CanExecute() => true; } diff --git a/src/App/Ui/MessageBox.cs b/src/App/Ui/MessageBox.cs index 7de18f0..ece27c9 100644 --- a/src/App/Ui/MessageBox.cs +++ b/src/App/Ui/MessageBox.cs @@ -1,6 +1,4 @@ -using Debianet.Abstractions; - -namespace Debianet.Ui; +namespace Debianet.Ui; internal sealed class MessageBox { diff --git a/src/App/Ui/TextViewer.cs b/src/App/Ui/TextViewer.cs new file mode 100644 index 0000000..1a4f022 --- /dev/null +++ b/src/App/Ui/TextViewer.cs @@ -0,0 +1,238 @@ +using System.Text; + +using Debianet.Abstractions; + +using Spectre.Console; + +namespace Debianet.Ui; + +internal sealed class TextViewer +{ + private const int TabSize = 8; + + private readonly Palete _palete; + private readonly IReadOnlyList _lines; + private readonly string _title; + + public TextViewer(string text, string? title = null, Palete? palete = null) + { + _lines = SplitIntoLines(text ?? string.Empty); + _title = title ?? string.Empty; + _palete = palete ?? Palete.Dracula; + } + + public void Show() + { + // Move to the alternate screen buffer so the original terminal content is preserved. + AnsiConsole.WriteLine("\e[?1049h"); + + try + { + Console.CursorVisible = false; + Console.Out.Write("\e[?7l\e[2J\e[H"); // disable line wrapping, clear, home + + Loop(); + } + finally + { + // Restore the main screen buffer first so the terminal is usable even on failure. + AnsiConsole.WriteLine("\e[?1049l"); + Console.Out.Write("\e[?7h"); // re-enable line wrapping + Console.CursorVisible = true; + } + } + + private void Loop() + { + int top = 0; + int renderedWidth = -1; + int renderedHeight = -1; + List visibleLines = []; + bool redraw = true; + + while (true) + { + int width = Math.Max(1, Console.WindowWidth); + int height = Math.Max(2, Console.WindowHeight); + int viewport = height - 1; // the last row is reserved for the status bar + + if (width != renderedWidth) + { + visibleLines = WrapLines(_lines, width); + renderedWidth = width; + redraw = true; + } + + if (height != renderedHeight) + { + renderedHeight = height; + redraw = true; + } + + int maxTop = Math.Max(0, visibleLines.Count - viewport); + top = Math.Clamp(top, 0, maxTop); + + if (redraw) + { + Render(visibleLines, top, viewport, width, height); + redraw = false; + } + + ConsoleKeyInfo key = Console.ReadKey(intercept: true); + switch (key.Key) + { + case ConsoleKey.Escape: + case ConsoleKey.Q: + return; + case ConsoleKey.UpArrow: + case ConsoleKey.K: + redraw = TryScroll(ref top, -1, maxTop); + break; + case ConsoleKey.DownArrow: + case ConsoleKey.J: + redraw = TryScroll(ref top, 1, maxTop); + break; + case ConsoleKey.PageUp: + redraw = TryScroll(ref top, -viewport, maxTop); + break; + case ConsoleKey.PageDown: + case ConsoleKey.Spacebar: + redraw = TryScroll(ref top, viewport, maxTop); + break; + case ConsoleKey.Home: + redraw = TryScroll(ref top, int.MinValue, maxTop); + break; + case ConsoleKey.End: + redraw = TryScroll(ref top, int.MaxValue, maxTop); + break; + } + } + } + + private static bool TryScroll(ref int top, int delta, int maxTop) + { + int target = delta switch + { + int.MinValue => 0, + int.MaxValue => maxTop, + _ => top + delta, + }; + + target = Math.Clamp(target, 0, maxTop); + if (target == top) + { + return false; + } + + top = target; + return true; + } + + private void Render(IReadOnlyList visibleLines, int top, int viewport, int width, int height) + { + StringBuilder frame = new(); + + for (int row = 0; row < viewport; row++) + { + frame.Append("\e[").Append(row + 1).Append(";1H"); // move the caret to the start of the row + + int index = top + row; + if (index < visibleLines.Count) + { + string line = visibleLines[index]; + frame.Append(line.Length > width ? line[..width] : line); + } + + frame.Append("\e[K"); // clear leftovers from the previous frame + } + + frame.Append("\e[").Append(height).Append(";1H"); // park the caret on the status row + Console.Out.Write(frame.ToString()); + + string status = BuildStatusBar(visibleLines.Count, top, viewport, width); + AnsiConsole.Markup($"[#{_palete.Accent.ToHex()} on #{_palete.Info.ToHex()}]{Markup.Escape(status)}[/]"); + } + + private string BuildStatusBar(int totalLines, int top, int viewport, int width) + { + string position; + if (totalLines == 0) + { + position = "(empty)"; + } + else + { + int first = top + 1; + int last = Math.Min(totalLines, top + viewport); + int maxTop = Math.Max(0, totalLines - viewport); + int percent = maxTop == 0 ? 100 : (int)Math.Round((100.0 * top) / maxTop); + position = $"Lines {first}-{last}/{totalLines} ({percent}%)"; + } + + string left = string.IsNullOrWhiteSpace(_title) ? position : $"{_title} | {position}"; + const string help = "Up/Down scroll PgUp/PgDn page Home/End jump Esc quit"; + + int gap = width - left.Length - help.Length; + string status = gap >= 2 ? $"{left}{new string(' ', gap)}{help}" : left; + + return status.Length > width ? status[..width] : status.PadRight(width); + } + + private static List WrapLines(IReadOnlyList lines, int width) + { + List wrapped = []; + + foreach (string raw in lines) + { + string line = ExpandAndSanitize(raw); + if (line.Length == 0) + { + wrapped.Add(string.Empty); + continue; + } + + for (int start = 0; start < line.Length; start += width) + { + int length = Math.Min(width, line.Length - start); + wrapped.Add(line.Substring(start, length)); + } + } + + return wrapped; + } + + private static string ExpandAndSanitize(string line) + { + StringBuilder builder = new(line.Length); + int column = 0; + + foreach (char c in line) + { + if (c == '\t') + { + int spaces = TabSize - (column % TabSize); + builder.Append(' ', spaces); + column += spaces; + } + else if (c < ' ' || c == '\u007F') + { + // Show control characters in caret notation so an embedded escape + // sequence cannot corrupt the rendered layout. + builder.Append('^').Append(c == '\u007F' ? '?' : (char)(c + '@')); + column += 2; + } + else + { + builder.Append(c); + column++; + } + } + + return builder.ToString(); + } + + private static List SplitIntoLines(string text) + => text.Length == 0 + ? [] + : [.. text.ReplaceLineEndings("\n").Split('\n')]; +} From 79221f22ef068c454129592f73f4b4e117d5c414 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sun, 21 Jun 2026 18:47:54 +0200 Subject: [PATCH 11/14] System Info command --- src/App/Abstractions/ITerminal.cs | 3 + src/App/Abstractions/Icons.cs | 1 + src/App/Abstractions/StringBuilderBuffer.cs | 41 +++++++++++ src/App/Abstractions/SystemInfoCollector.cs | 48 ++++++++++++ src/App/Abstractions/Terminal.cs | 18 +++++ src/App/Dto/LsCpuResult.cs | 25 +++++++ src/App/Menus/MainMenu.cs | 20 +++++ src/App/Program.cs | 10 +-- src/App/Properties/Resources.Designer.cs | 81 +++++++++++++++++++++ src/App/Properties/Resources.resx | 27 +++++++ src/App/Ui/DelegateTaskMenuItem.cs | 13 ++++ src/dev-app.sh | 3 + 12 files changed, 285 insertions(+), 5 deletions(-) create mode 100644 src/App/Abstractions/StringBuilderBuffer.cs create mode 100644 src/App/Abstractions/SystemInfoCollector.cs create mode 100644 src/App/Dto/LsCpuResult.cs create mode 100644 src/App/Ui/DelegateTaskMenuItem.cs create mode 100644 src/dev-app.sh diff --git a/src/App/Abstractions/ITerminal.cs b/src/App/Abstractions/ITerminal.cs index 47a0119..a7bafde 100644 --- a/src/App/Abstractions/ITerminal.cs +++ b/src/App/Abstractions/ITerminal.cs @@ -1,5 +1,7 @@ using Debianet.Ui; +using Spectre.Console.Rendering; + namespace Debianet.Abstractions; internal interface ITerminal @@ -18,4 +20,5 @@ internal interface ITerminal void WaitKey(); void Line(); void ShowMessageBox(MessageBox messageBox); + void ShowDialog(string dialogTitle, IRenderable content); } diff --git a/src/App/Abstractions/Icons.cs b/src/App/Abstractions/Icons.cs index 903a936..cc72e5f 100644 --- a/src/App/Abstractions/Icons.cs +++ b/src/App/Abstractions/Icons.cs @@ -10,4 +10,5 @@ internal static class Icons public const string Refresh = "🔄"; public const string Trash = "🗑️"; public const string Text = "📄"; + public const string Computer = "💻"; } diff --git a/src/App/Abstractions/StringBuilderBuffer.cs b/src/App/Abstractions/StringBuilderBuffer.cs new file mode 100644 index 0000000..0683d41 --- /dev/null +++ b/src/App/Abstractions/StringBuilderBuffer.cs @@ -0,0 +1,41 @@ +using System.Text; + +namespace Debianet.Abstractions; + +internal static class StringBuilderBuffer +{ + private static readonly StringBuilder _buffer = new StringBuilder(4096); + + public static StringBuilder GetBuffer() + { + return _buffer; + } + + public static string GetStringAndClear(int lines) + { + if (lines < 0) + { + string result = _buffer.ToString(); + _buffer.Clear(); + return result; + } + + for (int i=0; i<_buffer.Length; i++) + { + if (_buffer[i] == '\n') + { + lines--; + if (lines == 0) + { + string result = _buffer.ToString(0, i); + _buffer.Clear(); + return result; + } + } + } + + string remaining = _buffer.ToString(); + _buffer.Clear(); + return remaining; + } +} diff --git a/src/App/Abstractions/SystemInfoCollector.cs b/src/App/Abstractions/SystemInfoCollector.cs new file mode 100644 index 0000000..469995f --- /dev/null +++ b/src/App/Abstractions/SystemInfoCollector.cs @@ -0,0 +1,48 @@ +using System.Text.Json; + +using CliWrap; + +using Debianet.Dto; +using Debianet.Properties; + +namespace Debianet.Abstractions; + +internal static class SystemInfoCollector +{ + private static async Task RunAndGetStdout(string command, string[] arguments, int lines = -1) + { + if (command.StartsWith('$')) + { + command = Environment.GetEnvironmentVariable(command[1..]) ?? command; + } + + await Cli.Wrap(command) + .WithArguments(arguments) + .WithStandardOutputPipe(PipeTarget.ToStringBuilder(StringBuilderBuffer.GetBuffer())) + .ExecuteAsync(); + + return StringBuilderBuffer.GetStringAndClear(lines); + } + + + + public static async IAsyncEnumerable<(string property, string value)> CollectSystemInfoAsync() + { + yield return (Resources.SysInfo_HostName, Environment.MachineName); + yield return (Resources.SysInfo_Cpu, await GetCpuData()); + yield return (Resources.SysInfo_KernelVersion, await RunAndGetStdout("uname", ["-s", "-r"], 1)); + yield return (Resources.SysInfo_Uptime, await RunAndGetStdout("uptime", ["-p"], 1)); + yield return (Resources.SysInfo_Shell, await RunAndGetStdout("$SHELL", ["--version"], 1)); + yield return (Resources.SysInfo_Memory, await RunAndGetStdout("free", ["-h"])); + yield return (Resources.SysInfo_Disks, await RunAndGetStdout("df", ["-h", "-x", "tmpfs", "-x", "overlay", "-x", "devtmpfs", "--output=target,size,avail,pcent"])); + } + + private static async Task GetCpuData() + { + var lscpuJson = await RunAndGetStdout("lscpu", ["-J"]); + LsCpuResult? result = JsonSerializer.Deserialize(lscpuJson, JsonSerializerOptions.Web); + var cpuModel = result?.Lscpu.FirstOrDefault(x => x.Field == LsCpuResult.ModelName)?.Data; + return cpuModel != null ? cpuModel : "n/a"; + } +} + diff --git a/src/App/Abstractions/Terminal.cs b/src/App/Abstractions/Terminal.cs index 25bef2e..ccb49cf 100644 --- a/src/App/Abstractions/Terminal.cs +++ b/src/App/Abstractions/Terminal.cs @@ -1,6 +1,7 @@ using Debianet.Ui; using Spectre.Console; +using Spectre.Console.Rendering; namespace Debianet.Abstractions; @@ -81,4 +82,21 @@ public void ShowMessageBox(MessageBox messageBox) WaitKey(); SwitchToMainBuffer(); } + + public void ShowDialog(string dialogTitle, IRenderable content) + { + SwitchToAlternateBuffer(); + Clear(); + + var panel = new Panel(content) + .Header($"| {dialogTitle} |", Justify.Center) + .HeavyBorder() + .BorderColor(_palete.Accent) + .Expand(); + + AnsiConsole.Write(panel); + + WaitKey(); + SwitchToMainBuffer(); + } } \ No newline at end of file diff --git a/src/App/Dto/LsCpuResult.cs b/src/App/Dto/LsCpuResult.cs new file mode 100644 index 0000000..8fcea73 --- /dev/null +++ b/src/App/Dto/LsCpuResult.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace Debianet.Dto; + +public class LsCpuResult +{ + [JsonPropertyName("lscpu")] + public required FieldData[] Lscpu { get; init; } + + public class FieldData + { + [JsonPropertyName("field")] + public required string Field { get; init; } + + [JsonPropertyName("data")] + public required string Data { get; init; } + + public override string ToString() + { + return $"{Field}: {Data}"; + } + } + + public const string ModelName = "Model name:"; +} diff --git a/src/App/Menus/MainMenu.cs b/src/App/Menus/MainMenu.cs index 0c05559..3605858 100644 --- a/src/App/Menus/MainMenu.cs +++ b/src/App/Menus/MainMenu.cs @@ -2,6 +2,8 @@ using Debianet.Properties; using Debianet.Ui; +using Spectre.Console; + namespace Debianet.Menus; internal class MainMenu : Menu @@ -57,6 +59,12 @@ public override IEnumerable Items Text = Resources.MainMenu_DotnetToolsInstall, Submenu = MenuRegistry.DotnetToolInstaller }; + yield return new DelegateTaskMenuItem + { + Icon = Icons.Computer, + Task = SytemInfo, + Text = Resources.MainMenu_SystemInfo, + }; yield return new DelegateMenuItem { Text = Resources.MainMenu_Changelog, @@ -72,6 +80,18 @@ public override IEnumerable Items } } + private async Task SytemInfo(CancellationToken token) + { + var grid = new Grid(); + grid.AddColumn(); + grid.AddColumn(); + await foreach (var (property, value) in SystemInfoCollector.CollectSystemInfoAsync()) + { + grid.AddRow(property, value); + } + _terminal.ShowDialog(Resources.Dialog_Sysinfo, grid); + } + private void DisplayChangeLog() { var changelog = ResourceHandler.GetChangeLog(); diff --git a/src/App/Program.cs b/src/App/Program.cs index 784d3eb..ef1d49d 100644 --- a/src/App/Program.cs +++ b/src/App/Program.cs @@ -5,11 +5,11 @@ GlobalArgHandler.HandleGlobalArgs(args); -//var startupChecks = new StartupChecks(terminal); -//if (startupChecks.TryCheckExit(out int exitCode)) -//{ -// return exitCode; -//} +var startupChecks = new StartupChecks(terminal); +if (startupChecks.TryCheckExit(out int exitCode)) +{ + return exitCode; +} var registry = new MenuRegistry(terminal); var application = new Application(terminal, registry); diff --git a/src/App/Properties/Resources.Designer.cs b/src/App/Properties/Resources.Designer.cs index 60b57cf..ce60c56 100644 --- a/src/App/Properties/Resources.Designer.cs +++ b/src/App/Properties/Resources.Designer.cs @@ -69,6 +69,15 @@ internal static string App_MultiSelectionInstructions { } } + /// + /// Looks up a localized string similar to System Information. + /// + internal static string Dialog_Sysinfo { + get { + return ResourceManager.GetString("Dialog_Sysinfo", resourceCulture); + } + } + /// /// Looks up a localized string similar to Back to main menu. /// @@ -195,6 +204,15 @@ internal static string MainMenu_Item_Exit { } } + /// + /// Looks up a localized string similar to System Info.... + /// + internal static string MainMenu_SystemInfo { + get { + return ResourceManager.GetString("MainMenu_SystemInfo", resourceCulture); + } + } + /// /// Looks up a localized string similar to Debianet main menu. /// @@ -231,6 +249,69 @@ internal static string StartupCheck_RootUser { } } + /// + /// Looks up a localized string similar to Processor. + /// + internal static string SysInfo_Cpu { + get { + return ResourceManager.GetString("SysInfo_Cpu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disks. + /// + internal static string SysInfo_Disks { + get { + return ResourceManager.GetString("SysInfo_Disks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Host name. + /// + internal static string SysInfo_HostName { + get { + return ResourceManager.GetString("SysInfo_HostName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kernel Version. + /// + internal static string SysInfo_KernelVersion { + get { + return ResourceManager.GetString("SysInfo_KernelVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Memory. + /// + internal static string SysInfo_Memory { + get { + return ResourceManager.GetString("SysInfo_Memory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shell. + /// + internal static string SysInfo_Shell { + get { + return ResourceManager.GetString("SysInfo.Shell", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uptime. + /// + internal static string SysInfo_Uptime { + get { + return ResourceManager.GetString("SysInfo_Uptime", resourceCulture); + } + } + /// /// Looks up a localized string similar to Changelog. /// diff --git a/src/App/Properties/Resources.resx b/src/App/Properties/Resources.resx index 9fbf5b4..9a92b85 100644 --- a/src/App/Properties/Resources.resx +++ b/src/App/Properties/Resources.resx @@ -120,6 +120,9 @@ (Press <space> to select, <enter> to accept, <esc> to cancel) + + System Information + Back to main menu @@ -162,6 +165,9 @@ Exit + + System Info... + Debianet main menu @@ -174,6 +180,27 @@ Please run the application as a normal user. + + Shell + + + Processor + + + Disks + + + Host name + + + Kernel Version + + + Memory + + + Uptime + Changelog diff --git a/src/App/Ui/DelegateTaskMenuItem.cs b/src/App/Ui/DelegateTaskMenuItem.cs new file mode 100644 index 0000000..ce26fff --- /dev/null +++ b/src/App/Ui/DelegateTaskMenuItem.cs @@ -0,0 +1,13 @@ +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal sealed class DelegateTaskMenuItem : MenuItemBase +{ + public required Func Task { get; init; } + + public override async Task Execute(IApplication app, CancellationToken cancellationToken) + { + await Task(cancellationToken); + } +} \ No newline at end of file diff --git a/src/dev-app.sh b/src/dev-app.sh new file mode 100644 index 0000000..cf23d7d --- /dev/null +++ b/src/dev-app.sh @@ -0,0 +1,3 @@ +#!/bin/bash +dotnet build ./App/Debianet.csproj +./bin/Debug/Debianet From 2efee459a4753e0e7e9194162866c18bd3a9fb85 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Sun, 21 Jun 2026 19:05:29 +0200 Subject: [PATCH 12/14] Repo Restructure --- readme.md | 40 +- src/App/.gitignore | 482 ------------------ src/App/Debianet.csproj | 14 + src/{branding => Root/etc}/os-release.conf | 0 .../docker.service.d}/docker-override.conf | 0 .../etc}/wsl-distribution.conf | 0 .../usr/lib/wsl}/debianet.ico | 0 src/{branding => Root/usr/lib/wsl}/oobe.sh | 0 .../usr/lib/wsl}/terminal-debianet.json | 0 src/branding.sh | 21 - src/copy-files.sh | 11 + src/install-app.sh | 16 - src/install.sh | 6 - 13 files changed, 32 insertions(+), 558 deletions(-) delete mode 100644 src/App/.gitignore rename src/{branding => Root/etc}/os-release.conf (100%) rename src/{ => Root/etc/systemd/system/docker.service.d}/docker-override.conf (100%) rename src/{branding => Root/etc}/wsl-distribution.conf (100%) rename src/{branding => Root/usr/lib/wsl}/debianet.ico (100%) rename src/{branding => Root/usr/lib/wsl}/oobe.sh (100%) rename src/{branding => Root/usr/lib/wsl}/terminal-debianet.json (100%) delete mode 100644 src/branding.sh create mode 100644 src/copy-files.sh delete mode 100644 src/install-app.sh diff --git a/readme.md b/readme.md index 9a02c4c..6adddac 100644 --- a/readme.md +++ b/readme.md @@ -31,11 +31,9 @@ The complete list of changes can be found here: [changelog](changelog.md) - [DevToys](https://devtoys.app/) - [Dive](https://github.com/wagoodman/dive) - [docker](https://www.docker.com/) -- [FastFetch](https://github.com/fastfetch-cli/fastfetch) - [git](https://git-scm.com/) with [git-lfs](https://git-lfs.com/) - [htop](https://htop.dev/) - [just](https://github.com/casey/just) -- [lazygit](https://github.com/jesseduffield/lazygit) - [openssl](https://www.openssl.org/) - [ripgrep](https://github.com/BurntSushi/ripgrep) - [strace](https://strace.io/) @@ -49,28 +47,6 @@ The complete list of changes can be found here: [changelog](changelog.md) - [Visual studio remote shell](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging?view=visualstudio) - [Infer#](https://github.com/microsoft/infersharp) -**.NET tools from microsoft** - -- [dotnet-counters](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) -- [dotnet-coverage](https://learn.microsoft.com/en-us/dotnet/core/additional-tools/dotnet-coverage) -- [dotnet-ef](https://learn.microsoft.com/en-us/ef/core/cli/dotnet) -- [dotnet-gcdump](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-gcdump) -- [dotnet-monitor](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-monitor) -- [dotnet-stack](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-stack) -- [dotnet-symbol](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-symbol) -- [dotnet-trace](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace) -- [powershell](https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-linux) -- [slngen](https://github.com/microsoft/slngen) -- [upgrade-assistant](https://learn.microsoft.com/en-us/dotnet/core/porting/upgrade-assistant-overview) -- [DocFx](https://dotnet.github.io/docfx/index.html) - -**3rd party .NET tools** - -- [csharprepl](https://github.com/waf/CSharpRepl) -- [ilspycmd](github.com/icsharpcode/ILSpy) -- [roslynator](https://github.com/dotnet/roslynator?tab=readme-ov-file#command-line-tool) -- [Csproj](https://github.com/webmaster442/csproj) - ## Installation 1. Make sure you have HyperV and WSL installed & enabled. **Note: These commands require admin rights** @@ -95,12 +71,10 @@ wsl --set-default-version 2 1. Install debian: `wsl --install Debian` 2. Install user: `user` with password: `pass` -3. Run install.sh: `./src/install.sh` this will do most of the installing of software -4. Change to the source directory: `cd src` -5. Run branding.sh: `sudo ./branding.sh` to do branding -6. Run install-app.sh: `sudo ./install-app.sh` to install the debianet app. -7. exit: `exit` -8. do a shutdown: `wsl --shutdown` -9. export: `wsl --export Debian --format tar.xz debiannet.wsl` -10. unregister debian: `wsl --unregister Debian` -11. reinstall debianet.wsl \ No newline at end of file +3. Run commands: + +```bash +cd src +./install.sh +./copy-files.sh +``` \ No newline at end of file diff --git a/src/App/.gitignore b/src/App/.gitignore deleted file mode 100644 index 0808c4a..0000000 --- a/src/App/.gitignore +++ /dev/null @@ -1,482 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from `dotnet new gitignore` - -# dotenv files -.env - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET -project.lock.json -project.fragment.lock.json -artifacts/ - -# Tye -.tye/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -# but not Directory.Build.rsp, as it configures directory-level build defaults -!Directory.Build.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.tlog -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio 6 auto-generated project file (contains which files were open etc.) -*.vbp - -# Visual Studio 6 workspace and project file (working project files containing files to include in project) -*.dsw -*.dsp - -# Visual Studio 6 technical files -*.ncb -*.aps - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# Visual Studio History (VSHistory) files -.vshistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -# VS Code files for those working on multiple tools -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -# Local History for Visual Studio Code -.history/ - -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp - -# JetBrains Rider -*.sln.iml -.idea/ - -## -## Visual studio for Mac -## - - -# globs -Makefile.in -*.userprefs -*.usertasks -config.make -config.status -aclocal.m4 -install-sh -autom4te.cache/ -*.tar.gz -tarballs/ -test-results/ - -# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# Vim temporary swap files -*.swp diff --git a/src/App/Debianet.csproj b/src/App/Debianet.csproj index 30ee531..95c9e3e 100644 --- a/src/App/Debianet.csproj +++ b/src/App/Debianet.csproj @@ -9,12 +9,26 @@ $([System.DateTime]::UtcNow.ToString("yyyy")).$([System.DateTime]::UtcNow.ToString("MM")).$([System.DateTime]::UtcNow.ToString("dd")).0 false ..\bin\$(Configuration)\ + True + Webmaster442 + https://github.com/webmaster442/DebiaNet + debianet-128.png + https://github.com/webmaster442/DebiaNet + True + debianet + + + True + \ + + + diff --git a/src/branding/os-release.conf b/src/Root/etc/os-release.conf similarity index 100% rename from src/branding/os-release.conf rename to src/Root/etc/os-release.conf diff --git a/src/docker-override.conf b/src/Root/etc/systemd/system/docker.service.d/docker-override.conf similarity index 100% rename from src/docker-override.conf rename to src/Root/etc/systemd/system/docker.service.d/docker-override.conf diff --git a/src/branding/wsl-distribution.conf b/src/Root/etc/wsl-distribution.conf similarity index 100% rename from src/branding/wsl-distribution.conf rename to src/Root/etc/wsl-distribution.conf diff --git a/src/branding/debianet.ico b/src/Root/usr/lib/wsl/debianet.ico similarity index 100% rename from src/branding/debianet.ico rename to src/Root/usr/lib/wsl/debianet.ico diff --git a/src/branding/oobe.sh b/src/Root/usr/lib/wsl/oobe.sh similarity index 100% rename from src/branding/oobe.sh rename to src/Root/usr/lib/wsl/oobe.sh diff --git a/src/branding/terminal-debianet.json b/src/Root/usr/lib/wsl/terminal-debianet.json similarity index 100% rename from src/branding/terminal-debianet.json rename to src/Root/usr/lib/wsl/terminal-debianet.json diff --git a/src/branding.sh b/src/branding.sh deleted file mode 100644 index 324f097..0000000 --- a/src/branding.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -sudo rm /etc/wsl-distribution.conf -sudo cp ./branding/wsl-distribution.conf /etc/wsl-distribution.conf - -sudo rm /usr/lib/wsl/debian_logo.ico -sudo cp ./branding/debianet.ico /usr/lib/wsl/debianet.ico - -sudo rm /etc/os-release -sudo cp ./branding/os-release.conf /etc/os-release - -sudo rm /usr/lib/wsl/oobe.sh -sudo cp ./branding/oobe.sh /usr/lib/wsl/oobe.sh - -sudo cp ./branding/terminal-debianet.json /usr/lib/wsl/terminal-debianet.json - -sudo chmod 755 /etc/wsl-distribution.conf -sudo chmod 755 /usr/lib/wsl/oobe.sh -sudo chmod 755 /usr/lib/wsl/debianet.ico -sudo chmod 755 /usr/lib/wsl/terminal-debianet.json -sudo chmod 755 /etc/os-release diff --git a/src/copy-files.sh b/src/copy-files.sh new file mode 100644 index 0000000..101c7a6 --- /dev/null +++ b/src/copy-files.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +sudo rm /etc/wsl-distribution.conf +sudo rm /usr/lib/wsl/debian_logo.ico +sudo rm /usr/lib/wsl/oobe.sh +sudo mv /etc/os-release /etc/os_release.bak + +chmod 755 -R ./Root +cd /Root +cp -R -f ./etc /etc +cp -R -f ./usr /usr \ No newline at end of file diff --git a/src/install-app.sh b/src/install-app.sh deleted file mode 100644 index 0212ba0..0000000 --- a/src/install-app.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -sudo rm /usr/bin/debianet -sudo rm -r /opt/debianet-app/DebiaNetApp - -cd DebiaNetApp -sudo dotnet publish -r linux-x64 -c Release -o ./publish DebiaNetApp.csproj -sudo mkdir -p /opt/debianet-app -sudo cp -r ./publish/* /opt/debianet-app - -sudo rm -f /usr/bin/debianet -sudo ln -s /opt/debianet-app/DebiaNetApp /usr/bin/debianet -sudo chmod +x /usr/bin/debianet - -dotnet nuget locals all --clear -sudo dotnet nuget locals all --clear diff --git a/src/install.sh b/src/install.sh index 15cb4db..490ea00 100644 --- a/src/install.sh +++ b/src/install.sh @@ -18,13 +18,11 @@ sudo apt install -y \ binutils \ ca-certificates \ curl \ - fastfetch \ git \ git-lfs \ gpg \ htop \ just \ - lazygit \ libxml2 \ mc \ openssh-server \ @@ -64,10 +62,6 @@ sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin d # configure docker user sudo usermod -aG docker $USER -# enable docker tcp port for vs debug connecting -sudo mkdir -p /etc/systemd/system/docker.service.d/ -sudo cp docker-override.conf /etc/systemd/system/docker.service.d/override.conf - # install devtoys.cli wget https://github.com/DevToys-app/DevToys/releases/download/v2.0.8.0/devtoys.cli_linux_x64.deb -O devtoys.cli.deb sudo dpkg -i devtoys.cli.deb From 91e49fbf7a0a11b3558244eb85c9dd440f26d5a1 Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Tue, 23 Jun 2026 18:06:14 +0200 Subject: [PATCH 13/14] Progress on system menu and docker client --- src/App/Abstractions/DockerClient.cs | 53 ++++++++++++++++++++ src/App/MenuRegistry.cs | 2 + src/App/Menus/DotnetMenu.cs | 2 +- src/App/Menus/MainMenu.cs | 6 +++ src/App/Menus/SystemMenu.cs | 58 ++++++++++++++++++++++ src/App/Properties/Resources.Designer.cs | 63 ++++++++++++++++++++---- src/App/Properties/Resources.resx | 17 ++++++- src/App/Ui/RunCommandsMenuItem.cs | 37 ++++++++++++++ 8 files changed, 227 insertions(+), 11 deletions(-) create mode 100644 src/App/Abstractions/DockerClient.cs create mode 100644 src/App/Menus/SystemMenu.cs create mode 100644 src/App/Ui/RunCommandsMenuItem.cs diff --git a/src/App/Abstractions/DockerClient.cs b/src/App/Abstractions/DockerClient.cs new file mode 100644 index 0000000..4c6a1b0 --- /dev/null +++ b/src/App/Abstractions/DockerClient.cs @@ -0,0 +1,53 @@ +using System.Net.Sockets; + +namespace Debianet.Abstractions; + +internal sealed class DockerClient +{ + private const string SockerPath = "/var/run/docker.sock"; + private readonly SocketsHttpHandler _handler; + private readonly HttpClient _client; + private bool _disposed; + + public static bool IsDockerInstalled() + => File.Exists(SockerPath); + + public DockerClient() + { + _handler = new SocketsHttpHandler + { + ConnectCallback = async (context, cancellationToken) => + { + var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); + + await socket.ConnectAsync( + new UnixDomainSocketEndPoint(SockerPath), + cancellationToken); + + return new NetworkStream(socket, ownsSocket: true); + } + }; + _client = new HttpClient(_handler); + _client.BaseAddress = new Uri("http://localhost/v1.54/"); + } + + public async Task IsAccessible() + { + try + { + var response = await _client.GetAsync("_ping"); + return response.IsSuccessStatusCode; + } + catch (HttpRequestException) + { + return false; + } + } + + public void Dispose() + { + _client.Dispose(); + _handler.Dispose(); + _disposed = true; + } +} diff --git a/src/App/MenuRegistry.cs b/src/App/MenuRegistry.cs index 3fa8502..7ee9aa3 100644 --- a/src/App/MenuRegistry.cs +++ b/src/App/MenuRegistry.cs @@ -10,9 +10,11 @@ public MenuRegistry(ITerminal terminal) Main = new MainMenu(this, terminal); Dotnet = new DotnetMenu(this, terminal); DotnetToolInstaller = new DotnetToolsInstallerMenu(this, terminal); + System = new SystemMenu(this, terminal); } public MainMenu Main { get; } public DotnetMenu Dotnet { get; } public DotnetToolsInstallerMenu DotnetToolInstaller { get; } + public SystemMenu System { get; } } diff --git a/src/App/Menus/DotnetMenu.cs b/src/App/Menus/DotnetMenu.cs index e2622ae..4ae2308 100644 --- a/src/App/Menus/DotnetMenu.cs +++ b/src/App/Menus/DotnetMenu.cs @@ -29,7 +29,7 @@ public override IEnumerable Items yield return new ReplaceMenuMenuItem { Icon = Icons.Back, - Text = Resources.DotnetMenu_Item_Back, + Text = Resources.Menu_Item_Back, Submenu = MenuRegistry.Main }; yield return new RunCommandMenuItem(_terminal) diff --git a/src/App/Menus/MainMenu.cs b/src/App/Menus/MainMenu.cs index 3605858..98e2541 100644 --- a/src/App/Menus/MainMenu.cs +++ b/src/App/Menus/MainMenu.cs @@ -59,6 +59,12 @@ public override IEnumerable Items Text = Resources.MainMenu_DotnetToolsInstall, Submenu = MenuRegistry.DotnetToolInstaller }; + yield return new ReplaceMenuMenuItem + { + Icon = Icons.SubMenu, + Text = Resources.MainMenu_System, + Submenu = MenuRegistry.System, + }; yield return new DelegateTaskMenuItem { Icon = Icons.Computer, diff --git a/src/App/Menus/SystemMenu.cs b/src/App/Menus/SystemMenu.cs new file mode 100644 index 0000000..009b8c5 --- /dev/null +++ b/src/App/Menus/SystemMenu.cs @@ -0,0 +1,58 @@ +using Debianet.Abstractions; +using Debianet.Properties; +using Debianet.Ui; + +namespace Debianet.Menus; + +internal sealed class SystemMenu : Menu +{ + private readonly ITerminal _terminal; + + public SystemMenu(MenuRegistry menuRegistry, ITerminal terminal) : base(menuRegistry) + { + _terminal = terminal; + } + + public override string Title + => Resources.SystemMenu_Title; + + public override IEnumerable Items + { + get + { + yield return new ReplaceMenuMenuItem + { + Icon = Icons.Back, + Text = Resources.Menu_Item_Back, + Submenu = MenuRegistry.Main + }; + yield return new RunCommandsMenuItem(_terminal) + { + Text = Resources.SystemMenu_AptClean, + Commands = + [ + ("sudo", ["apt", "autoremove"]), + ("sudo", ["apt", "clean"]) + ] + }; + yield return new RunCommandsMenuItem(_terminal) + { + Text = Resources.SystemMenu_Update, + Commands = + [ + ("sudo", ["apt", "update"]), + ("sudo", ["apt", "upgrade", "-y"]) + ] + }; + yield return new RunCommandsMenuItem(_terminal) + { + Text = Resources.SystemMenu_InstallCpp, + Commands = + [ + ("sudo", ["apt", "update"]), + ("sudo", ["apt", "install", "-y", "build-essential", "cmake", "gcc", "g++", "libtool"]) + ] + }; + } + } +} \ No newline at end of file diff --git a/src/App/Properties/Resources.Designer.cs b/src/App/Properties/Resources.Designer.cs index ce60c56..9cdfe69 100644 --- a/src/App/Properties/Resources.Designer.cs +++ b/src/App/Properties/Resources.Designer.cs @@ -78,15 +78,6 @@ internal static string Dialog_Sysinfo { } } - /// - /// Looks up a localized string similar to Back to main menu. - /// - internal static string DotnetMenu_Item_Back { - get { - return ResourceManager.GetString("DotnetMenu_Item_Back", resourceCulture); - } - } - /// /// Looks up a localized string similar to Clear all local NuGet caches.... /// @@ -204,6 +195,15 @@ internal static string MainMenu_Item_Exit { } } + /// + /// Looks up a localized string similar to System.... + /// + internal static string MainMenu_System { + get { + return ResourceManager.GetString("MainMenu_System", resourceCulture); + } + } + /// /// Looks up a localized string similar to System Info.... /// @@ -222,6 +222,15 @@ internal static string MainMenu_Title { } } + /// + /// Looks up a localized string similar to Back to main menu. + /// + internal static string Menu_Item_Back { + get { + return ResourceManager.GetString("Menu_Item_Back", resourceCulture); + } + } + /// /// Looks up a localized string similar to Error. /// @@ -312,6 +321,42 @@ internal static string SysInfo_Uptime { } } + /// + /// Looks up a localized string similar to Clean apt cache.... + /// + internal static string SystemMenu_AptClean { + get { + return ResourceManager.GetString("SystemMenu_AptClean", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Install c/c++ dev tools. + /// + internal static string SystemMenu_InstallCpp { + get { + return ResourceManager.GetString("SystemMenu_InstallCpp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to System. + /// + internal static string SystemMenu_Title { + get { + return ResourceManager.GetString("SystemMenu_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to System update.... + /// + internal static string SystemMenu_Update { + get { + return ResourceManager.GetString("SystemMenu_Update", resourceCulture); + } + } + /// /// Looks up a localized string similar to Changelog. /// diff --git a/src/App/Properties/Resources.resx b/src/App/Properties/Resources.resx index 9a92b85..e392dd7 100644 --- a/src/App/Properties/Resources.resx +++ b/src/App/Properties/Resources.resx @@ -123,7 +123,7 @@ System Information - + Back to main menu @@ -201,7 +201,22 @@ Uptime + + System + Changelog + + Clean apt cache... + + + System update... + + + Install c/c++ dev tools + + + System... + \ No newline at end of file diff --git a/src/App/Ui/RunCommandsMenuItem.cs b/src/App/Ui/RunCommandsMenuItem.cs new file mode 100644 index 0000000..b11f535 --- /dev/null +++ b/src/App/Ui/RunCommandsMenuItem.cs @@ -0,0 +1,37 @@ +using CliWrap; + +using Debianet.Abstractions; + +namespace Debianet.Ui; + +internal sealed class RunCommandsMenuItem(ITerminal terminal) : MenuItemBase +{ + public required IEnumerable<(string program, string[] arguments)> Commands { get; init; } + + public override async Task Execute(IApplication app, CancellationToken cancellationToken) + { + TimeSpan totalTime = TimeSpan.Zero; + foreach (var (program, arguments) in Commands) + { + terminal.Info($"Executing {program} {string.Join(' ', arguments)} ..."); + terminal.Line(); + + var result = await Cli.Wrap(program) + .WithArguments(arguments) + .WithStandardOutputPipe(PipeTarget.ToDelegate(terminal.StandardOutput)) + .WithStandardErrorPipe(PipeTarget.ToDelegate(terminal.StandardError)) + .ExecuteAsync(cancellationToken); + + if (result.ExitCode != 0) + { + terminal.Error($"Command {program} {string.Join(' ', arguments)} failed with exit code {result.ExitCode}"); + break; + } + + totalTime += result.RunTime; + terminal.Line(); + } + terminal.Info($"Total run time: {totalTime.FormatTime()}"); + terminal.WaitKey(); + } +} From bb5a82d31020a12910ac4a614e23bf9d4002903e Mon Sep 17 00:00:00 2001 From: webmaster442 Date: Tue, 23 Jun 2026 18:13:21 +0200 Subject: [PATCH 14/14] Docker menu progress --- src/App/Abstractions/DockerClient.cs | 2 +- src/App/Abstractions/IDockerClient.cs | 6 +++++ src/App/MenuRegistry.cs | 4 ++- src/App/Menus/DockerMenu.cs | 34 ++++++++++++++++++++++++ src/App/Menus/MainMenu.cs | 15 ++++++++++- src/App/Program.cs | 3 ++- src/App/Properties/Resources.Designer.cs | 18 +++++++++++++ src/App/Properties/Resources.resx | 6 +++++ 8 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/App/Abstractions/IDockerClient.cs create mode 100644 src/App/Menus/DockerMenu.cs diff --git a/src/App/Abstractions/DockerClient.cs b/src/App/Abstractions/DockerClient.cs index 4c6a1b0..79ffd8b 100644 --- a/src/App/Abstractions/DockerClient.cs +++ b/src/App/Abstractions/DockerClient.cs @@ -2,7 +2,7 @@ namespace Debianet.Abstractions; -internal sealed class DockerClient +internal sealed class DockerClient : IDisposable, IDockerClient { private const string SockerPath = "/var/run/docker.sock"; private readonly SocketsHttpHandler _handler; diff --git a/src/App/Abstractions/IDockerClient.cs b/src/App/Abstractions/IDockerClient.cs new file mode 100644 index 0000000..a77b178 --- /dev/null +++ b/src/App/Abstractions/IDockerClient.cs @@ -0,0 +1,6 @@ +namespace Debianet.Abstractions; + +internal interface IDockerClient +{ + Task IsAccessible(); +} \ No newline at end of file diff --git a/src/App/MenuRegistry.cs b/src/App/MenuRegistry.cs index 7ee9aa3..5749b3a 100644 --- a/src/App/MenuRegistry.cs +++ b/src/App/MenuRegistry.cs @@ -5,16 +5,18 @@ namespace Debianet; internal class MenuRegistry { - public MenuRegistry(ITerminal terminal) + public MenuRegistry(ITerminal terminal, DockerClient dockerClient) { Main = new MainMenu(this, terminal); Dotnet = new DotnetMenu(this, terminal); DotnetToolInstaller = new DotnetToolsInstallerMenu(this, terminal); System = new SystemMenu(this, terminal); + Docker = new DockerMenu(this, dockerClient); } public MainMenu Main { get; } public DotnetMenu Dotnet { get; } public DotnetToolsInstallerMenu DotnetToolInstaller { get; } public SystemMenu System { get; } + public DockerMenu Docker { get; } } diff --git a/src/App/Menus/DockerMenu.cs b/src/App/Menus/DockerMenu.cs new file mode 100644 index 0000000..d56bdf1 --- /dev/null +++ b/src/App/Menus/DockerMenu.cs @@ -0,0 +1,34 @@ +using Debianet.Abstractions; +using Debianet.Properties; +using Debianet.Ui; + +namespace Debianet.Menus; + +internal class DockerMenu : Menu +{ + private readonly ITerminal _terminal; + private readonly IDockerClient _dockerClient; + + public DockerMenu(MenuRegistry menuRegistry, ITerminal terminal, IDockerClient dockerClient) + : base(menuRegistry) + { + _terminal = terminal; + _dockerClient = dockerClient; + } + + public override string Title + => Resources.DockerMenu_Title; + + public override IEnumerable Items + { + get + { + yield return new ReplaceMenuMenuItem + { + Icon = Icons.Back, + Text = Resources.Menu_Item_Back, + Submenu = MenuRegistry.Main + }; + } + } +} diff --git a/src/App/Menus/MainMenu.cs b/src/App/Menus/MainMenu.cs index 98e2541..3fb8a37 100644 --- a/src/App/Menus/MainMenu.cs +++ b/src/App/Menus/MainMenu.cs @@ -9,11 +9,13 @@ namespace Debianet.Menus; internal class MainMenu : Menu { private readonly ITerminal _terminal; + private readonly IDockerClient _dockerClient; - public MainMenu(MenuRegistry menuRegistry, ITerminal terminal) + public MainMenu(MenuRegistry menuRegistry, ITerminal terminal, IDockerClient dockerClient) : base(menuRegistry) { _terminal = terminal; + _dockerClient = dockerClient; } public override string Title @@ -65,6 +67,17 @@ public override IEnumerable Items Text = Resources.MainMenu_System, Submenu = MenuRegistry.System, }; + + if (DockerClient.IsDockerInstalled()) + { + yield return new ReplaceMenuMenuItem + { + Icon = Icons.SubMenu, + Text = Resources.MainMenu_Docker, + Submenu = new DockerMenu(MenuRegistry, _terminal, _dockerClient), + }; + } + yield return new DelegateTaskMenuItem { Icon = Icons.Computer, diff --git a/src/App/Program.cs b/src/App/Program.cs index ef1d49d..5b5bbb0 100644 --- a/src/App/Program.cs +++ b/src/App/Program.cs @@ -2,6 +2,7 @@ using Debianet.Abstractions; var terminal = new Terminal(); +using var dockerClient = new DockerClient(); GlobalArgHandler.HandleGlobalArgs(args); @@ -11,7 +12,7 @@ return exitCode; } -var registry = new MenuRegistry(terminal); +var registry = new MenuRegistry(terminal, dockerClient); var application = new Application(terminal, registry); await application.Run(); diff --git a/src/App/Properties/Resources.Designer.cs b/src/App/Properties/Resources.Designer.cs index 9cdfe69..a81bd11 100644 --- a/src/App/Properties/Resources.Designer.cs +++ b/src/App/Properties/Resources.Designer.cs @@ -78,6 +78,15 @@ internal static string Dialog_Sysinfo { } } + /// + /// Looks up a localized string similar to Docker. + /// + internal static string DockerMenu_Title { + get { + return ResourceManager.GetString("DockerMenu_Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Clear all local NuGet caches.... /// @@ -168,6 +177,15 @@ internal static string MainMenu_Changelog { } } + /// + /// Looks up a localized string similar to Docker.... + /// + internal static string MainMenu_Docker { + get { + return ResourceManager.GetString("MainMenu_Docker", resourceCulture); + } + } + /// /// Looks up a localized string similar to Install .NET Tools.... /// diff --git a/src/App/Properties/Resources.resx b/src/App/Properties/Resources.resx index e392dd7..7c66626 100644 --- a/src/App/Properties/Resources.resx +++ b/src/App/Properties/Resources.resx @@ -219,4 +219,10 @@ System... + + Docker + + + Docker... + \ No newline at end of file