Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Product>SQL Schema Compare</Product>
<Description>https://github.com/TiCodeX/SQLSchemaCompare</Description>
<Authors>TiCodeX</Authors>
<Version>2026.5.1</Version>
<Version>2026.6.1</Version>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
2 changes: 1 addition & 1 deletion SQLSchemaCompare.CLI/Commands/CompareCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected override int Execute(CommandContext context, Options options, Cancella
projectService.Project.TargetProviderOptions.ConnectionString = options.TargetConnectionString;
}

databaseCompareService.StartCompare();
databaseCompareService.StartCompare(false);

while (!taskService.CurrentTaskInfos.All(x => x.Status is TaskStatus.RanToCompletion or TaskStatus.Faulted or TaskStatus.Canceled))
{
Expand Down
117 changes: 117 additions & 0 deletions SQLSchemaCompare.CLI/Commands/MonitorCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
namespace TiCodeX.SQLSchemaCompare.CLI.Commands;

using System.Reflection;

/// <summary>
/// The monitor command
/// </summary>
internal class MonitorCommand(IProjectService projectService, ITaskService taskService, IDatabaseCompareService databaseCompareService, ILogger<MonitorCommand> logger)
: Command<MonitorCommand.Options>
{
/// <inheritdoc/>
protected override int Execute(CommandContext context, Options options, CancellationToken cancellationToken)
{
projectService.NewProject(options.DatabaseType);
projectService.Project.SourceProviderOptions.UseConnectionString = true;
projectService.Project.SourceProviderOptions.ConnectionString = options.ConnectionString;
projectService.Project.TargetProviderOptions.UseConnectionString = true;
projectService.Project.TargetProviderOptions.ConnectionString = options.ConnectionString;

databaseCompareService.StartCompare(true);

while (!taskService.CurrentTaskInfos.All(x => x.Status is TaskStatus.RanToCompletion or TaskStatus.Faulted or TaskStatus.Canceled))
{
Thread.Sleep(200);
}

if (taskService.CurrentTaskInfos.Any(x => x.Status is TaskStatus.Faulted or TaskStatus.Canceled))
{
var exception = taskService.CurrentTaskInfos.FirstOrDefault(x => x.Status is TaskStatus.Faulted or TaskStatus.Canceled)?.Exception;
if (exception != null)
{
throw exception;
}

throw new InvalidOperationException("Unknown error during compare task");
}

File.WriteAllText(options.OutputFile, projectService.Project.Result.FullAlterScript);
logger.LogInformation("Compare completed successfully. Output written to {OutputFile}", options.OutputFile);

return 0;
}

/// <summary>
/// The options for the compare command
/// </summary>
internal sealed class Options : CommandSettings
{
/// <summary>
/// Gets the type of the database.
/// </summary>
[OptionGroup("Inline options")]
[CommandOption("--type <TYPE>")]
[Description("The database type")]
public DatabaseType DatabaseType { get; init; } = (DatabaseType)(-1);

/// <summary>
/// Gets the connection string.
/// </summary>
[OptionGroup("Inline options")]
[CommandOption("--connection <CONNECTION_STRING>")]
[Description("The connection string")]
public string ConnectionString { get; init; }

/// <summary>
/// Gets the output file.
/// </summary>
[OptionGroup("Common options")]
[CommandOption("-o|--output <FILE_PATH>")]
[Description("The output file")]
public string OutputFile { get; init; }

/// <inheritdoc/>
public override ValidationResult Validate()
{
var hasDatabaseType = this.DatabaseType != (DatabaseType)(-1);
var hasConnectionString = !string.IsNullOrWhiteSpace(this.ConnectionString);
var hasOutputFile = !string.IsNullOrWhiteSpace(this.OutputFile);

if (!hasDatabaseType && !hasConnectionString && !hasOutputFile)
{
throw new ShowHelpException();
}

var missingOptions = new List<string>();

if (!hasDatabaseType)
{
missingOptions.Add(GetLongName(nameof(this.DatabaseType)));
}

if (!hasConnectionString)
{
missingOptions.Add(GetLongName(nameof(this.ConnectionString)));
}

if (!hasOutputFile)
{
missingOptions.Add(GetLongName(nameof(this.OutputFile)));
}

if (missingOptions.Count > 0)
{
return ValidationResult.Error($"Missing required options: {string.Join(", ", missingOptions)}");
}

return ValidationResult.Success();

static string GetLongName(string propertyName)
{
var propertyInfo = typeof(Options).GetProperty(propertyName);
var commandOption = propertyInfo.GetCustomAttribute<CommandOptionAttribute>();
return $"--{commandOption.LongNames[0]}";
}
}
}
}
6 changes: 5 additions & 1 deletion SQLSchemaCompare.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ public static int Main(string[] args)
{
var services = new ServiceCollection()
.RegisterServices()
.AddLogging();
.AddLogging(builder =>
{
builder.AddConsole();
});

using var registrar = new DependencyInjectionRegistrar(services);

Expand All @@ -27,6 +30,7 @@ public static int Main(string[] args)
config.SetHelpProvider(new CustomHelpProvider(config.Settings));

config.AddCommand<CompareCommand>("compare").WithDescription("Compare two databases.");
config.AddCommand<MonitorCommand>("monitor").WithDescription("Monitor database changes.");
});

// When options are provided without a command name, default to 'compare'
Expand Down
5 changes: 3 additions & 2 deletions SQLSchemaCompare.CLI/SQLSchemaCompare.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.9" />
<PackageReference Include="Neolution.CodeAnalysis" Version="3.3.0-beta.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Spectre.Console" Version="0.55.2" />
<PackageReference Include="Spectre.Console" Version="0.57.1" />
<PackageReference Include="Spectre.Console.Cli" Version="0.55.0" />
<PackageReference Include="Spectre.Console.Cli.Extensions.DependencyInjection" Version="0.25.0" />
<PackageReference Include="Spectre.Console.Cli.Extensions.DependencyInjection" Version="0.26.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading