From 09f21b87e84314f3648551998d5176a3e1e7f623 Mon Sep 17 00:00:00 2001 From: Micaiah Martin Date: Fri, 17 Apr 2026 10:33:28 -0600 Subject: [PATCH 1/5] Add -o --output parameters and a new JSON arg, which provides machine readable output when piping into other tooling --- util/SeederUtility/Commands/PresetArgs.cs | 20 ++++++++++++++++++++ util/SeederUtility/Commands/PresetCommand.cs | 18 +++++++++++++++--- util/SeederUtility/Program.cs | 6 +++++- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/util/SeederUtility/Commands/PresetArgs.cs b/util/SeederUtility/Commands/PresetArgs.cs index d9bc8af37ebe..dcd4522167ef 100644 --- a/util/SeederUtility/Commands/PresetArgs.cs +++ b/util/SeederUtility/Commands/PresetArgs.cs @@ -2,6 +2,12 @@ namespace Bit.SeederUtility.Commands; +public enum OutputFormat +{ + Text, + Json, +} + /// /// CLI argument model for the preset command. /// Supports loading presets from embedded resources. @@ -14,6 +20,14 @@ public class PresetArgs : IArgumentModel [Option('l', "list", Description = "List all available presets and fixtures")] public bool List { get; set; } + [Option('o', "output", Description = "Output format for --list: text or json (default: text)")] + public string? Output { get; set; } + + public OutputFormat GetOutputFormat() => + string.IsNullOrWhiteSpace(Output) + ? OutputFormat.Text + : Enum.Parse(Output, ignoreCase: true); + [Option("mangle", Description = "Enable mangling for test isolation")] public bool Mangle { get; set; } @@ -25,6 +39,12 @@ public class PresetArgs : IArgumentModel public void Validate() { + if (!string.IsNullOrWhiteSpace(Output) + && !Enum.TryParse(Output, ignoreCase: true, out _)) + { + throw new ArgumentException($"Unrecognized output format '{Output}'. Allowed: text, json."); + } + if (List) { return; diff --git a/util/SeederUtility/Commands/PresetCommand.cs b/util/SeederUtility/Commands/PresetCommand.cs index 48c35ef1a443..8fa66d918b59 100644 --- a/util/SeederUtility/Commands/PresetCommand.cs +++ b/util/SeederUtility/Commands/PresetCommand.cs @@ -1,4 +1,5 @@ -using Bit.Seeder.Recipes; +using System.Text.Json; +using Bit.Seeder.Recipes; using Bit.Seeder.Services; using Bit.SeederUtility.Configuration; using Bit.SeederUtility.Helpers; @@ -18,7 +19,7 @@ public void Execute(PresetArgs args) if (args.List) { - PrintAvailablePresets(); + PrintAvailablePresets(args.GetOutputFormat()); return; } @@ -89,7 +90,7 @@ private static void RunIndividualPreset(PresetArgs args) ConsoleOutput.PrintMangleMap(deps); } - private static void PrintAvailablePresets() + private static void PrintAvailablePresets(OutputFormat format = OutputFormat.Text) { var available = PresetCatalogService.ListAvailable(); @@ -108,6 +109,17 @@ private static void PrintAvailablePresets() } } + if (format == OutputFormat.Json) + { + var output = new + { + organization = orgPresets, + individual = individualPresets, + }; + Console.WriteLine(JsonSerializer.Serialize(output, new JsonSerializerOptions { WriteIndented = true })); + return; + } + Console.WriteLine("Organization Presets:"); foreach (var preset in orgPresets) { diff --git a/util/SeederUtility/Program.cs b/util/SeederUtility/Program.cs index b9abfb5d7b13..c49b65b465e9 100644 --- a/util/SeederUtility/Program.cs +++ b/util/SeederUtility/Program.cs @@ -7,7 +7,11 @@ public class Program { private static int Main(string[] args) { - PrintBanner(); + // Skip the banner when stdout is piped or redirected + if (!Console.IsOutputRedirected) + { + PrintBanner(); + } return new AppRunner() .Run(args); From c7f7ac8befbf562a971c946e6231fe334180c907 Mon Sep 17 00:00:00 2001 From: MtnBurrit0 <77340197+mimartin12@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:03:26 -0600 Subject: [PATCH 2/5] Update util/SeederUtility/Commands/PresetArgs.cs Co-authored-by: Mick Letofsky --- util/SeederUtility/Commands/PresetArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/SeederUtility/Commands/PresetArgs.cs b/util/SeederUtility/Commands/PresetArgs.cs index dcd4522167ef..e9bc47bf658c 100644 --- a/util/SeederUtility/Commands/PresetArgs.cs +++ b/util/SeederUtility/Commands/PresetArgs.cs @@ -20,7 +20,7 @@ public class PresetArgs : IArgumentModel [Option('l', "list", Description = "List all available presets and fixtures")] public bool List { get; set; } - [Option('o', "output", Description = "Output format for --list: text or json (default: text)")] + [Option("output", Description = "Output format for --list: text or json (default: text)")] public string? Output { get; set; } public OutputFormat GetOutputFormat() => From 1eec9ca8448a1c8704cb8040599a674a7e227999 Mon Sep 17 00:00:00 2001 From: Micaiah Martin Date: Wed, 22 Apr 2026 10:42:06 -0600 Subject: [PATCH 3/5] Write to Console.Error --- util/SeederUtility/Program.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/util/SeederUtility/Program.cs b/util/SeederUtility/Program.cs index c49b65b465e9..2b0a245f8fd3 100644 --- a/util/SeederUtility/Program.cs +++ b/util/SeederUtility/Program.cs @@ -7,11 +7,7 @@ public class Program { private static int Main(string[] args) { - // Skip the banner when stdout is piped or redirected - if (!Console.IsOutputRedirected) - { - PrintBanner(); - } + PrintBanner(); return new AppRunner() .Run(args); @@ -77,13 +73,13 @@ private static void PrintBanner() continue; } - Console.WriteLine($"{color}{line[minIndent..]}{reset}"); + Console.Error.WriteLine($"{color}{line[minIndent..]}{reset}"); } } - Console.WriteLine($" {bold}{cyan}╔══════════════════════════════════════════╗{reset}"); - Console.WriteLine($" {bold}{cyan}║ SEEDER UTILITY ║{reset}"); - Console.WriteLine($" {bold}{cyan}╚══════════════════════════════════════════╝{reset}"); + Console.Error.WriteLine($" {bold}{cyan}╔══════════════════════════════════════════╗{reset}"); + Console.Error.WriteLine($" {bold}{cyan}║ SEEDER UTILITY ║{reset}"); + Console.Error.WriteLine($" {bold}{cyan}╚══════════════════════════════════════════╝{reset}"); } [Subcommand] From 3d3c0e11fa87d755d42e704fb2a5673468d0abd2 Mon Sep 17 00:00:00 2001 From: Micaiah Martin Date: Wed, 22 Apr 2026 10:42:59 -0600 Subject: [PATCH 4/5] Add OutputFormat enum into it's own file --- util/SeederUtility/Commands/OutputFormat.cs | 7 +++++++ util/SeederUtility/Commands/PresetArgs.cs | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 util/SeederUtility/Commands/OutputFormat.cs diff --git a/util/SeederUtility/Commands/OutputFormat.cs b/util/SeederUtility/Commands/OutputFormat.cs new file mode 100644 index 000000000000..e4f098c36e43 --- /dev/null +++ b/util/SeederUtility/Commands/OutputFormat.cs @@ -0,0 +1,7 @@ +namespace Bit.SeederUtility.Commands; + +public enum OutputFormat +{ + Text, + Json, +} diff --git a/util/SeederUtility/Commands/PresetArgs.cs b/util/SeederUtility/Commands/PresetArgs.cs index e9bc47bf658c..d36051043a38 100644 --- a/util/SeederUtility/Commands/PresetArgs.cs +++ b/util/SeederUtility/Commands/PresetArgs.cs @@ -2,12 +2,6 @@ namespace Bit.SeederUtility.Commands; -public enum OutputFormat -{ - Text, - Json, -} - /// /// CLI argument model for the preset command. /// Supports loading presets from embedded resources. From 75ae63ba3c69d221a4e837912d2301db64598508 Mon Sep 17 00:00:00 2001 From: Micaiah Martin Date: Wed, 22 Apr 2026 10:49:55 -0600 Subject: [PATCH 5/5] fmt fix --- util/SeederUtility/Commands/OutputFormat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/SeederUtility/Commands/OutputFormat.cs b/util/SeederUtility/Commands/OutputFormat.cs index e4f098c36e43..b23ea97d276b 100644 --- a/util/SeederUtility/Commands/OutputFormat.cs +++ b/util/SeederUtility/Commands/OutputFormat.cs @@ -1,4 +1,4 @@ -namespace Bit.SeederUtility.Commands; +namespace Bit.SeederUtility.Commands; public enum OutputFormat {