diff options
Diffstat (limited to 'src/Commands')
-rw-r--r-- | src/Commands/BaseCommand.cs | 116 | ||||
-rw-r--r-- | src/Commands/BuildCommand.cs | 66 | ||||
-rw-r--r-- | src/Commands/CleanCommand.cs | 31 | ||||
-rw-r--r-- | src/Commands/PublishCommand.cs | 80 | ||||
-rw-r--r-- | src/Commands/TestCommand.cs | 31 | ||||
-rw-r--r-- | src/Commands/TestDisplayCommand.cs | 22 | ||||
-rw-r--r-- | src/Commands/UpdateCommand.cs | 25 |
7 files changed, 371 insertions, 0 deletions
diff --git a/src/Commands/BaseCommand.cs b/src/Commands/BaseCommand.cs new file mode 100644 index 0000000..6362c22 --- /dev/null +++ b/src/Commands/BaseCommand.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using Typin; +using Typin.Console; +using Typin.Attributes; + +using VNLib.Tools.Build.Executor.Model; +using VNLib.Tools.Build.Executor.Constants; +using static VNLib.Tools.Build.Executor.Constants.Config; + +namespace VNLib.Tools.Build.Executor.Commands +{ + public abstract class BaseCommand(BuildPipeline pipeline, ConfigManager bm) : ICommand + { + [CommandOption("verbose", 'v', Description = "Prints verbose output")] + public bool Verbose { get; set; } + + [CommandOption("force", 'f', Description = "Forces the operation even if steps are required")] + public bool Force { get; set; } + + [CommandOption("modules", 'm', Description = "Only use the specified modules, comma separated list")] + public string? Modules { get; set; } + + [CommandOption("exclude", 'x', Description = "Ignores the specified modules, comma separated list")] + public string? Exclude { get; set; } + + [CommandOption("confirm", 'c', Description = "Wait for user input before continuing")] + public bool Confirm { get; set; } + + //Allow users to specify build directory + [CommandOption("build-dir", 'B', Description = "Sets the base build directory to execute operations in")] + public string BuildDir { get; set; } = Directory.GetCurrentDirectory(); + + [CommandOption("max-logs", 'L', Description = "Sets the maximum number of logs to keep")] + public int MaxLogs { get; set; } = 50; + + public IDirectoryIndex BuildIndex { get; private set; } = default!; + + public BuildConfig Config { get; private set; } = default!; + + + /* + * Base exec command does basic init and cleanup on startup + */ + + public virtual async ValueTask ExecuteAsync(IConsole console) + { + try + { + CancellationToken ct = console.GetCancellationToken(); + + //Init build index + BuildIndex = GetIndex(); + Config = await bm.GetOrCreateConfig(BuildIndex, false); + + //Cleanup log files on init + TrimLogs(BuildIndex, MaxLogs); + + string[] modules = Modules?.Split(',') ?? []; + string[] exclude = Exclude?.Split(',') ?? []; + + //Always load the pipeline + await pipeline.LoadAsync(Config, modules, exclude, Feeds); + + if (Confirm) + { + console.Output.WriteLine("---- Pipeline loaded. Press any key to continue ----"); + await console.Input.ReadLineAsync(ct); + + await Task.Delay(100, ct); + ct.ThrowIfCancellationRequested(); + } + + //Exec steps then exit + await ExecStepsAsync(console); + } + catch (OperationCanceledException) + { + console.WithForegroundColor(ConsoleColor.Red, static o => o.Output.WriteLine("Operation cancelled")); + } + catch(BuildStepFailedException be) + { + console.WithForegroundColor(ConsoleColor.Red, o => o.Output.WriteLine("FATAL: Build step failed on module {0}, msg -> {1}", be.ArtifactName, be.Message)); + } + } + + public abstract ValueTask ExecStepsAsync(IConsole console); + + public abstract IFeedManager[] Feeds { get; } + + private Dirs GetIndex() => new() + { + BaseDir = new(BuildDir), + BuildDir = BuildDirs.GetOrCreateDir(BUILD_DIR_NAME), + LogDir = BuildDirs.GetOrCreateDir(LOG_DIR_NAME), + ScratchDir = BuildDirs.GetOrCreateDir(SCRATCH_DIR), + SumDir = BuildDirs.GetOrCreateDir(SUM_DIR), + OutputDir = BuildDirs.GetOrCreateDir(OUTPUT_DIR) + }; + +#nullable disable + protected sealed class Dirs : IDirectoryIndex + { + public DirectoryInfo BaseDir { get; set; } + public DirectoryInfo BuildDir { get; set; } + public DirectoryInfo LogDir { get; set; } + public DirectoryInfo ScratchDir { get; set; } + public DirectoryInfo SumDir { get; set; } + public DirectoryInfo OutputDir { get; set; } + } +#nullable enable + } +}
\ No newline at end of file diff --git a/src/Commands/BuildCommand.cs b/src/Commands/BuildCommand.cs new file mode 100644 index 0000000..c1d9828 --- /dev/null +++ b/src/Commands/BuildCommand.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +using Typin.Console; +using Typin.Attributes; + +using VNLib.Tools.Build.Executor.Model; +using VNLib.Tools.Build.Executor.Constants; + +namespace VNLib.Tools.Build.Executor.Commands +{ + + [Command("build", Description = "Executes a build operation in pipeline")] + public class BuildCommand(BuildPipeline pipeline, ConfigManager bm) : BaseCommand(pipeline, bm) + { + + [CommandOption("no-delay", 'S', Description = "Skips any built-in delay/wait")] + public bool SkipDelay { get; set; } = false; + + public override async ValueTask ExecStepsAsync(IConsole console) + { + CancellationToken cancellation = console.GetCancellationToken(); + + console.Output.WriteLine("Starting build pipeline. Checking for source code changes"); + + if (Force) + { + console.WithForegroundColor(ConsoleColor.Yellow, static o => o.Output.WriteLine("Forcing build step")); + } + + //Check for source code changes + bool changed = await pipeline.CheckForChangesAsync(); + + //continue build + if (!Force && !changed) + { + console.WithForegroundColor(ConsoleColor.Green, static o => o.Output.WriteLine("No source code changes detected. Skipping build step")); + return; + } + + if (Confirm) + { + console.Output.WriteLine("Press any key to continue..."); + await console.Input.ReadLineAsync(cancellation); + cancellation.ThrowIfCancellationRequested(); + } + else if(!SkipDelay) + { + //wait for 10 seconds + for (int i = 10; i > 0; i--) + { + string seconds = i > 1 ? "seconds" : "second"; + console.Output.WriteLine($"Starting build step in {i} {seconds}"); + await Task.Delay(1000, cancellation); + } + } + + await pipeline.DoStepBuild(Force); + + console.WithForegroundColor(ConsoleColor.Green, static o => o.Output.WriteLine("Build completed successfully")); + } + + public override IFeedManager[] Feeds => []; + } +}
\ No newline at end of file diff --git a/src/Commands/CleanCommand.cs b/src/Commands/CleanCommand.cs new file mode 100644 index 0000000..6570b31 --- /dev/null +++ b/src/Commands/CleanCommand.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; + +using Typin.Console; +using Typin.Attributes; + +using VNLib.Tools.Build.Executor.Model; +using VNLib.Tools.Build.Executor.Constants; + +namespace VNLib.Tools.Build.Executor.Commands +{ + + [Command("clean", Description = "Cleans the build pipeline")] + public sealed class CleanCommand(BuildPipeline pipeline, ConfigManager cfg) : BaseCommand(pipeline, cfg) + { + public override async ValueTask ExecStepsAsync(IConsole console) + { + console.Output.WriteLine("Begining clean step"); + + await pipeline.DoStepCleanAsync(); + + //Cleanup the sum dir and scratch dir + Config.Index.SumDir.Delete(true); + Config.Index.ScratchDir.Delete(true); + + console.WithForegroundColor(ConsoleColor.Green, o => o.Output.WriteLine("Pipeline cleaned")); + } + + public override IFeedManager[] Feeds => []; + } +}
\ No newline at end of file diff --git a/src/Commands/PublishCommand.cs b/src/Commands/PublishCommand.cs new file mode 100644 index 0000000..4179f86 --- /dev/null +++ b/src/Commands/PublishCommand.cs @@ -0,0 +1,80 @@ + +using System; +using System.Linq; +using System.Threading.Tasks; + +using Typin.Console; +using Typin.Attributes; + +using VNLib.Tools.Build.Executor.Model; +using VNLib.Tools.Build.Executor.Constants; + +namespace VNLib.Tools.Build.Executor.Commands +{ + [Command("publish", Description = "Runs publishig build steps on a completed build")] + public sealed class PublishCommand(BuildPipeline pipeline, ConfigManager bm) : BaseCommand(pipeline, bm) + { + [CommandOption("upload-path", 'p', Description = "The path to upload the build artifacts")] + public string? UploadPath { get; set; } + + [CommandOption("sign", 's', Description = "Enables gpg signing of build artifacts")] + public bool Sign { get; set; } = false; + + [CommandOption("gpg-key", 'k', Description = "Optional key to use when signing, otherwise uses the GPG default signing key")] + public string? GpgKey { get; set; } + + [CommandOption("sleet-path", 'F', Description = "Specifies the Sleet feed index path")] + public string? SleetPath { get; set; } + + [CommandOption("dry-run", 'd', Description = "Executes all publish steps without pushing the changes to the remote server")] + public bool DryRun { get; set; } + + [CommandOption("output", 'o', Description = "Specifies the output directory for the published modules")] + public string? CustomOutDir { get; set; } + + public override async ValueTask ExecStepsAsync(IConsole console) + { + //Specify custom output dir + (base.Config.Index as Dirs)!.OutputDir = BuildDirs.GetOrCreateDir(Constants.Config.OUTPUT_DIR, CustomOutDir); + + IUploadManager? uploads = MinioUploadManager.Create(UploadPath); + IFeedManager? feed = Feeds.FirstOrDefault(); + + //Optional gpg signer for signing published artifacts + BuildPublisher pub = new(Config, new GpgSigner(Sign, GpgKey)); + + console.WithForegroundColor(ConsoleColor.DarkGreen, static o => o.Output.WriteLine("Publishing modules")); + + //Run publish steps + await pipeline.OnPublishingAsync().ConfigureAwait(false); + + console.WithForegroundColor(ConsoleColor.DarkGreen, static o => o.Output.WriteLine("Preparing module output for upload")); + + //Prepare the output + await pipeline.PrepareOutputAsync(pub).ConfigureAwait(false); + + if(uploads is null) + { + console.WithForegroundColor(ConsoleColor.DarkYellow, static o => o.Output.WriteLine("No upload path specified. Skipping upload")); + console.WithForegroundColor(ConsoleColor.Green, static o => o.Output.WriteLine("Upload build complete")); + return; + } + + //Run upload + await pipeline.ManualUpload(pub, uploads).ConfigureAwait(false); + + //Publish feeds + if (feed is not null) + { + console.WithForegroundColor(ConsoleColor.DarkGreen, static o => o.Output.WriteLine("Uploading feeds...")); + + //Exec feed upload + await uploads.UploadDirectoryAsync(feed.FeedOutputDir); + } + + console.WithForegroundColor(ConsoleColor.Green, static o => o.Output.WriteLine("Upload build complete")); + } + + public override IFeedManager[] Feeds => SleetPath is null ? [] : [SleetFeedManager.GetSleetFeed(SleetPath)]; + } +}
\ No newline at end of file diff --git a/src/Commands/TestCommand.cs b/src/Commands/TestCommand.cs new file mode 100644 index 0000000..d64fe64 --- /dev/null +++ b/src/Commands/TestCommand.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; + +using Typin.Console; +using Typin.Attributes; + +using VNLib.Tools.Build.Executor.Model; +using VNLib.Tools.Build.Executor.Constants; + +namespace VNLib.Tools.Build.Executor.Commands +{ + [Command("test", Description = "Executes tests steps within the pipline for all loaded modules")] + public sealed class TestCommand(BuildPipeline pipeline, ConfigManager cfg) : BaseCommand(pipeline, cfg) + { + private readonly BuildPipeline _pipeline = pipeline; + + [CommandOption("--no-fail", Description = "Exit testing on the first test failure")] + public bool FailOnTestFail { get; set; } = true; + + public override async ValueTask ExecStepsAsync(IConsole console) + { + console.Output.WriteLine("Begining test step"); + + await _pipeline.ExecuteTestsAsync(FailOnTestFail); + + console.WithForegroundColor(ConsoleColor.Green, o => o.Output.WriteLine("Pipeline tests compled")); + } + + public override IFeedManager[] Feeds => []; + } +}
\ No newline at end of file diff --git a/src/Commands/TestDisplayCommand.cs b/src/Commands/TestDisplayCommand.cs new file mode 100644 index 0000000..c716ada --- /dev/null +++ b/src/Commands/TestDisplayCommand.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +using Typin.Console; +using Typin.Attributes; + +using VNLib.Tools.Build.Executor.Model; +using VNLib.Tools.Build.Executor.Constants; + +namespace VNLib.Tools.Build.Executor.Commands +{ + [Command("display", Description = "Test command for debugging")] + public sealed class TestDisplayCommand(BuildPipeline pipeline, ConfigManager bm) : BaseCommand(pipeline, bm) + { + public override IFeedManager[] Feeds => []; + + public override async ValueTask ExecStepsAsync(IConsole console) + { + console.Output.WriteLine("Press any key to exit..."); + await console.Input.ReadLineAsync(console.GetCancellationToken()); + } + } +}
\ No newline at end of file diff --git a/src/Commands/UpdateCommand.cs b/src/Commands/UpdateCommand.cs new file mode 100644 index 0000000..e3bdf9c --- /dev/null +++ b/src/Commands/UpdateCommand.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading.Tasks; + +using Typin.Console; +using Typin.Attributes; + +using VNLib.Tools.Build.Executor.Model; +using VNLib.Tools.Build.Executor.Constants; + +namespace VNLib.Tools.Build.Executor.Commands +{ + [Command("update", Description = "Runs the build steps for updating application soure code")] + public sealed class UpdateCommand(BuildPipeline pipeline, ConfigManager cfg) : BaseCommand(pipeline, cfg) + { + public override async ValueTask ExecStepsAsync(IConsole console) + { + //Run the update step + await pipeline.DoStepUpdateSource(); + + console.WithForegroundColor(ConsoleColor.Green, o => o.Output.WriteLine("Source update complete")); + } + + public override IFeedManager[] Feeds => []; + } +}
\ No newline at end of file |