diff --git a/yuniql-cli/CommandLineService.cs b/yuniql-cli/CommandLineService.cs index 4ec5250f..6841151a 100644 --- a/yuniql-cli/CommandLineService.cs +++ b/yuniql-cli/CommandLineService.cs @@ -139,7 +139,7 @@ public int RunMigration(RunOption opts) appliedByTool: toolName, appliedByToolVersion: toolVersion, environmentCode: opts.Environment, - opts.ContinueAfterFailure ? NonTransactionalResolvingOption.ContinueAfterFailure : (NonTransactionalResolvingOption?) null, + opts.ContinueAfterFailure ? NonTransactionalResolvingOption.ContinueAfterFailure : (NonTransactionalResolvingOption?)null, opts.NoTransaction ); @@ -247,15 +247,14 @@ public int RunListOption(ListOption opts) var migrationService = _migrationServiceFactory.Create(opts.Platform); migrationService.Initialize(opts.ConnectionString, opts.CommandTimeout); var versions = migrationService.GetAllVersions(opts.MetaSchema, opts.MetaTable); + + var versionPrettyPrint = new TablePrinter("SchemaVersion", "AppliedOnUtc", "Status", "AppliedByUser", "AppliedByTool"); + versions.ForEach(v => versionPrettyPrint.AddRow(v.Version, v.AppliedOnUtc.ToString("u"), v.Status, v.AppliedByUser, $"{v.AppliedByTool} {v.AppliedByToolVersion}")); + versionPrettyPrint.Print(); + + _traceService.Success($"Listed all schema versions applied to database on {opts.Path} workspace.{Environment.NewLine}" + + $"For platforms not supporting full transactional DDL operations (ex. MySql, CockroachDB, Snowflake), unsuccessful migrations will show the status as Failed and you can look for LastFailedScript and LastScriptError in the schema version tracking table."); - var results = new StringBuilder(); - results.AppendLine($"Version\t\tCreated\t\t\t\tCreatedBy"); - versions.ForEach(v => - { - results.AppendLine($"{v.Version}\t\t{v.AppliedOnUtc.ToString("u")}\t{v.AppliedByUser}"); - }); - - Console.WriteLine(results.ToString()); return 0; } catch (Exception ex) @@ -346,7 +345,7 @@ public int RunArchiveOption(ArchiveOption opts) } } - private int OnException(Exception exception, string headerMessage, bool debug, ITraceService traceService) + private int OnException(Exception exception, string headerMessage, bool debug, ITraceService traceService) { var userMessage = debug ? exception.ToString() : $"{exception.Message} {exception.InnerException?.Message}"; traceService.Error($"{headerMessage}. Arrg... something seems broken.{Environment.NewLine}" + @@ -354,5 +353,5 @@ private int OnException(Exception exception, string headerMessage, bool debug, I $"If you think this is a bug, please report an issue here /~https://github.com/rdagumampan/yuniql/issues."); return 1; } - } + } } diff --git a/yuniql-cli/TablePrinter.cs b/yuniql-cli/TablePrinter.cs new file mode 100644 index 00000000..efd039ed --- /dev/null +++ b/yuniql-cli/TablePrinter.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Yuniql.CLI +{ + //thanks https://stackoverflow.com/users/1547699/sumudu + //https://stackoverflow.com/a/54943087/3449591 + public class TablePrinter + { + private readonly string[] titles; + private readonly List lengths; + private readonly List rows = new List(); + + public TablePrinter(params string[] titles) + { + this.titles = titles; + lengths = titles.Select(t => t.Length).ToList(); + } + + public void AddRow(params object[] row) + { + if (row.Length != titles.Length) + { + throw new Exception($"Added row length [{row.Length}] is not equal to title row length [{titles.Length}]"); + } + rows.Add(row.Select(o => o.ToString()).ToArray()); + for (int i = 0; i < titles.Length; i++) + { + if (rows.Last()[i].Length > lengths[i]) + { + lengths[i] = rows.Last()[i].Length; + } + } + } + + public void Print() + { + lengths.ForEach(l => System.Console.Write("+-" + new string('-', l) + '-')); + Console.WriteLine("+"); + + string line = ""; + for (int i = 0; i < titles.Length; i++) + { + line += "| " + titles[i].PadRight(lengths[i]) + ' '; + } + Console.WriteLine(line + "|"); + + lengths.ForEach(l => System.Console.Write("+-" + new string('-', l) + '-')); + Console.WriteLine("+"); + + foreach (var row in rows) + { + line = ""; + for (int i = 0; i < row.Length; i++) + { + if (int.TryParse(row[i], out int n)) + { + line += "| " + row[i].PadLeft(lengths[i]) + ' '; // numbers are padded to the left + } + else + { + line += "| " + row[i].PadRight(lengths[i]) + ' '; + } + } + Console.WriteLine(line + "|"); + } + + lengths.ForEach(l => System.Console.Write("+-" + new string('-', l) + '-')); + Console.WriteLine("+"); + Console.WriteLine(); + } + } +} diff --git a/yuniql-extensibility/DbVersion.cs b/yuniql-extensibility/DbVersion.cs index 1555512a..c51aaa71 100644 --- a/yuniql-extensibility/DbVersion.cs +++ b/yuniql-extensibility/DbVersion.cs @@ -20,7 +20,7 @@ public class DbVersion /// /// The date and time in UTC when migration was run. /// - public DateTime AppliedOnUtc { get; set; } + public DateTime AppliedOnUtc { get; set; } = DateTime.UtcNow; /// /// The user id used when migration was performed. @@ -31,17 +31,17 @@ public class DbVersion /// The yuniql client that executed the migration step. /// This can be yuniql-cli, yuniql-aspnetcore, yuniql-core, yuniql-azdevops /// - public string AppliedByTool { get; set; } + public string AppliedByTool { get; set; } = "yuniql-cli"; /// /// The version of client that executed the migration step. /// - public string AppliedByToolVersion { get; set; } + public string AppliedByToolVersion { get; set; } = typeof(DbVersion).Assembly.GetName().Version.ToString(); /// /// Additional information that describes the execution of the version /// - public string AdditionalArtifacts { get; set; } + public string AdditionalArtifacts { get; set; } = string.Empty; /// /// The status of version execution @@ -51,11 +51,11 @@ public class DbVersion /// /// The full path of last failed script file /// - public string FailedScriptPath { get; set; } + public string FailedScriptPath { get; set; } = string.Empty; /// /// The error details from the last failed script file /// - public string FailedScriptError { get; set; } + public string FailedScriptError { get; set; } = string.Empty; } }