From 98ab3e42917255969aecc4d4b44eb8fe5669ea78 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 9 May 2022 11:09:17 +0300 Subject: [PATCH 1/2] Cli: Create migrations after project generation --- .../CreateMigrationAndRunMigratorCommand.cs | 100 ++-------------- .../Volo/Abp/Cli/Commands/NewCommand.cs | 6 +- .../Commands/ProjectCreationCommandBase.cs | 41 ++++++- .../Commands/Services/DotnetEfToolManager.cs | 43 +++++++ .../Services/InitialMigrationCreator.cs | 111 ++++++++++++++++++ 5 files changed, 211 insertions(+), 90 deletions(-) create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/DotnetEfToolManager.cs create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/InitialMigrationCreator.cs diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs index 6519ea70f9..38491551a8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CreateMigrationAndRunMigratorCommand.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.Commands.Services; using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; @@ -12,14 +13,18 @@ namespace Volo.Abp.Cli.Commands; public class CreateMigrationAndRunMigratorCommand : IConsoleCommand, ITransientDependency { + private readonly InitialMigrationCreator _ınıtialMigrationCreator; public const string Name = "create-migration-and-run-migrator"; public ICmdHelper CmdHelper { get; } + public DotnetEfToolManager DotnetEfToolManager { get; } public ILogger Logger { get; set; } - public CreateMigrationAndRunMigratorCommand(ICmdHelper cmdHelper) + public CreateMigrationAndRunMigratorCommand(ICmdHelper cmdHelper, InitialMigrationCreator ınıtialMigrationCreator, DotnetEfToolManager dotnetEfToolManager) { + _ınıtialMigrationCreator = ınıtialMigrationCreator; CmdHelper = cmdHelper; + DotnetEfToolManager = dotnetEfToolManager; Logger = NullLogger.Instance; } @@ -39,110 +44,31 @@ public class CreateMigrationAndRunMigratorCommand : IConsoleCommand, ITransientD throw new Exception("DbMigrator is not found!"); } - if (!IsDotNetEfToolInstalled()) - { - InstallDotnetEfTool(); - } + await DotnetEfToolManager.BeSureInstalledAsync(); - var tenantDbContextName = FindTenantDbContextName(dbMigrationsFolder); - var dbContextName = tenantDbContextName != null ? - FindDbContextName(dbMigrationsFolder) - : null; + var migrationsCreatedSuccessfully = await _ınıtialMigrationCreator.CreateAsync(commandLineArgs.Target, !nolayers); - var migrationOutput = AddMigrationAndGetOutput(dbMigrationsFolder, dbContextName, "Migrations"); - var tenantMigrationOutput = tenantDbContextName != null ? - AddMigrationAndGetOutput(dbMigrationsFolder, tenantDbContextName, "TenantMigrations") - : null; - - if (CheckMigrationOutput(migrationOutput) && CheckMigrationOutput(tenantMigrationOutput)) + if (migrationsCreatedSuccessfully) { if (nolayers) { - // Migration added successfully - CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(Path.Combine(dbMigrationsFolder, "MyCompanyName.MyProjectName")) + "\" && dotnet run --migrate-database"); + CmdHelper.RunCmd("dotnet run --migrate-database", Path.GetDirectoryName(Path.Combine(dbMigrationsFolder, "MyCompanyName.MyProjectName"))); } else { - // Migration added successfully - CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProjectPath) + "\" && dotnet run"); + CmdHelper.RunCmd("dotnet run", Path.GetDirectoryName(dbMigratorProjectPath)); } await Task.CompletedTask; } else { - var exceptionMsg = "Migrations failed! A migration command didn't run successfully:" + - Environment.NewLine + - Environment.NewLine + migrationOutput + - Environment.NewLine + - Environment.NewLine + tenantMigrationOutput; + var exceptionMsg = "Migrations failed! A migration command didn't run successfully."; Logger.LogError(exceptionMsg); throw new Exception(exceptionMsg); } } - - private string FindTenantDbContextName(string dbMigrationsFolder) - { - var tenantDbContext = Directory.GetFiles(dbMigrationsFolder, "*TenantMigrationsDbContext.cs", SearchOption.AllDirectories) - .FirstOrDefault() ?? - Directory.GetFiles(dbMigrationsFolder, "*TenantDbContext.cs", SearchOption.AllDirectories) - .FirstOrDefault(); - - if (tenantDbContext == null) - { - return null; - } - - return Path.GetFileName(tenantDbContext).RemovePostFix(".cs"); - } - - private string FindDbContextName(string dbMigrationsFolder) - { - var dbContext = Directory.GetFiles(dbMigrationsFolder, "*MigrationsDbContext.cs", SearchOption.AllDirectories) - .FirstOrDefault(fp => !fp.EndsWith("TenantMigrationsDbContext.cs")) ?? - Directory.GetFiles(dbMigrationsFolder, "*DbContext.cs", SearchOption.AllDirectories) - .FirstOrDefault(fp => !fp.EndsWith("TenantDbContext.cs")); - - if (dbContext == null) - { - return null; - } - - return Path.GetFileName(dbContext).RemovePostFix(".cs"); - } - - private string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbContext, string outputDirectory) - { - var dbContextOption = string.IsNullOrWhiteSpace(dbContext) - ? string.Empty - : $"--context {dbContext}"; - - var addMigrationCmd = $"cd \"{dbMigrationsFolder}\" && " + - $"dotnet ef migrations add Initial --output-dir {outputDirectory} {dbContextOption}"; - - return CmdHelper.RunCmdAndGetOutput(addMigrationCmd, out int exitCode); - } - - private bool IsDotNetEfToolInstalled() - { - var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g", out int exitCode); - return output.Contains("dotnet-ef"); - } - - private static bool CheckMigrationOutput(string output) - { - return output == null || (output.Contains("Done.") && - output.Contains("To undo this action") && - output.Contains("ef migrations remove")); - } - - private void InstallDotnetEfTool() - { - Logger.LogInformation("Installing dotnet-ef tool..."); - CmdHelper.RunCmd("dotnet tool install --global dotnet-ef"); - Logger.LogInformation("dotnet-ef tool is installed."); - } - + private static string GetDbMigratorProjectPath(string dbMigrationsFolderPath) { var srcFolder = Directory.GetParent(dbMigrationsFolderPath); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 526ae77363..2974e01fd4 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -32,8 +32,9 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien SolutionPackageVersionFinder solutionPackageVersionFinder, ICmdHelper cmdHelper, IInstallLibsService installLibsService, - AngularPwaSupportAdder angularPwaSupportAdder) - : base(connectionStringProvider, solutionPackageVersionFinder, cmdHelper, installLibsService, angularPwaSupportAdder) + AngularPwaSupportAdder angularPwaSupportAdder, + InitialMigrationCreator ınitialMigrationCreator) + : base(connectionStringProvider, solutionPackageVersionFinder, cmdHelper, installLibsService, angularPwaSupportAdder, ınitialMigrationCreator) { TemplateProjectBuilder = templateProjectBuilder; TemplateInfoProvider = templateInfoProvider; @@ -79,6 +80,7 @@ public class NewCommand : ProjectCreationCommandBase, IConsoleCommand, ITransien Logger.LogInformation($"'{projectName}' has been successfully created to '{projectArgs.OutputFolder}'"); RunGraphBuildForMicroserviceServiceTemplate(projectArgs); + await CreateInitialMigrationsAsync(projectArgs); await RunInstallLibsForWebTemplateAsync(projectArgs); ConfigurePwaSupportForAngular(projectArgs); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs index 6e280d7dc3..74a23a6cbc 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs @@ -6,6 +6,7 @@ using ICSharpCode.SharpZipLib.Core; using ICSharpCode.SharpZipLib.Zip; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using NUglify.Helpers; using Volo.Abp.Cli.ProjectModification; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Commands.Services; @@ -26,6 +27,7 @@ public abstract class ProjectCreationCommandBase public ICmdHelper CmdHelper { get; } public IInstallLibsService InstallLibsService { get; } public AngularPwaSupportAdder AngularPwaSupportAdder { get; } + public InitialMigrationCreator InitialMigrationCreator { get; } public ILogger Logger { get; set; } public ProjectCreationCommandBase( @@ -33,13 +35,15 @@ public abstract class ProjectCreationCommandBase SolutionPackageVersionFinder solutionPackageVersionFinder, ICmdHelper cmdHelper, IInstallLibsService installLibsService, - AngularPwaSupportAdder angularPwaSupportAdder) + AngularPwaSupportAdder angularPwaSupportAdder, + InitialMigrationCreator ınitialMigrationCreator) { ConnectionStringProvider = connectionStringProvider; SolutionPackageVersionFinder = solutionPackageVersionFinder; CmdHelper = cmdHelper; InstallLibsService = installLibsService; AngularPwaSupportAdder = angularPwaSupportAdder; + InitialMigrationCreator = ınitialMigrationCreator; Logger = NullLogger.Instance; } @@ -337,6 +341,41 @@ public abstract class ProjectCreationCommandBase } } + protected async Task CreateInitialMigrationsAsync(ProjectBuildArgs projectArgs) + { + if (projectArgs.DatabaseProvider == DatabaseProvider.MongoDb) + { + return; + } + + var efCoreProjectPath = string.Empty; + bool isLayeredTemplate; + + switch (projectArgs.TemplateName) + { + case AppTemplate.TemplateName: + case AppProTemplate.TemplateName: + efCoreProjectPath = Directory.GetFiles(projectArgs.OutputFolder, "*EntityFrameworkCore.csproj", SearchOption.AllDirectories).FirstOrDefault(); + isLayeredTemplate = true; + break; + case AppNoLayersTemplate.TemplateName: + case AppNoLayersProTemplate.TemplateName: + efCoreProjectPath = Directory.GetFiles(projectArgs.OutputFolder, "*.csproj", SearchOption.AllDirectories).FirstOrDefault(); + isLayeredTemplate = false; + break; + default: + return; + } + + if (string.IsNullOrWhiteSpace(efCoreProjectPath)) + { + Logger.LogWarning("Couldn't find EntityFrameworkCore project to create initial migrations!"); + return; + } + + await InitialMigrationCreator.CreateAsync(Path.GetDirectoryName(efCoreProjectPath), isLayeredTemplate); + } + protected void ConfigurePwaSupportForAngular(ProjectBuildArgs projectArgs) { var isAngular = projectArgs.UiFramework == UiFramework.Angular; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/DotnetEfToolManager.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/DotnetEfToolManager.cs new file mode 100644 index 0000000000..cbeb6ee9d4 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/DotnetEfToolManager.cs @@ -0,0 +1,43 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands.Services; + +public class DotnetEfToolManager : ISingletonDependency +{ + public ICmdHelper CmdHelper { get; } + public ILogger Logger { get; set; } + + public DotnetEfToolManager(ICmdHelper cmdHelper) + { + CmdHelper = cmdHelper; + + Logger = NullLogger.Instance; + } + + public async Task BeSureInstalledAsync() + { + if (IsDotNetEfToolInstalled()) + { + return; + } + + InstallDotnetEfTool(); + } + + private bool IsDotNetEfToolInstalled() + { + var output = CmdHelper.RunCmdAndGetOutput("dotnet tool list -g"); + return output.Contains("dotnet-ef"); + } + + private void InstallDotnetEfTool() + { + Logger.LogInformation("Installing dotnet-ef tool..."); + CmdHelper.RunCmd("dotnet tool install --global dotnet-ef"); + Logger.LogInformation("dotnet-ef tool is installed."); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/InitialMigrationCreator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/InitialMigrationCreator.cs new file mode 100644 index 0000000000..860fcef60f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/InitialMigrationCreator.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands.Services; + +public class InitialMigrationCreator : ITransientDependency +{ + public ICmdHelper CmdHelper { get; } + public DotnetEfToolManager DotnetEfToolManager { get; } + public ILogger Logger { get; set; } + + public InitialMigrationCreator(ICmdHelper cmdHelper, DotnetEfToolManager dotnetEfToolManager) + { + CmdHelper = cmdHelper; + DotnetEfToolManager = dotnetEfToolManager; + + Logger = NullLogger.Instance; + } + + public async Task CreateAsync(string targetProjectFolder, bool layeredTemplate = true) + { + if (targetProjectFolder == null || !Directory.Exists(targetProjectFolder)) + { + Logger.LogError($"This path doesn't exist: {targetProjectFolder}"); + return false; + } + + Logger.LogInformation("Creating initial migrations..."); + + await DotnetEfToolManager.BeSureInstalledAsync(); + + var tenantDbContextName = FindTenantDbContextName(targetProjectFolder); + var dbContextName = tenantDbContextName != null ? + FindDbContextName(targetProjectFolder) + : null; + + var migrationOutput = AddMigrationAndGetOutput(targetProjectFolder, dbContextName, "Migrations"); + var tenantMigrationOutput = tenantDbContextName != null ? + AddMigrationAndGetOutput(targetProjectFolder, tenantDbContextName, "TenantMigrations") + : null; + + var migrationSuccess = CheckMigrationOutput(migrationOutput) && CheckMigrationOutput(tenantMigrationOutput); + + if (migrationSuccess) + { + Logger.LogInformation("Initial migrations are created."); + } + else + { + Logger.LogError("Creating initial migrations process is failed! Details:" + Environment.NewLine + + migrationOutput + Environment.NewLine + + tenantMigrationOutput + Environment.NewLine); + } + + return migrationSuccess; + } + + private string FindTenantDbContextName(string projectFolder) + { + var tenantDbContext = Directory.GetFiles(projectFolder, "*TenantMigrationsDbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault() ?? + Directory.GetFiles(projectFolder, "*TenantDbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault(); + + if (tenantDbContext == null) + { + return null; + } + + return Path.GetFileName(tenantDbContext).RemovePostFix(".cs"); + } + + private string FindDbContextName(string projectFolder) + { + var dbContext = Directory.GetFiles(projectFolder, "*MigrationsDbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault(fp => !fp.EndsWith("TenantMigrationsDbContext.cs")) ?? + Directory.GetFiles(projectFolder, "*DbContext.cs", SearchOption.AllDirectories) + .FirstOrDefault(fp => !fp.EndsWith("TenantDbContext.cs")); + + if (dbContext == null) + { + return null; + } + + return Path.GetFileName(dbContext).RemovePostFix(".cs"); + } + + private string AddMigrationAndGetOutput(string dbMigrationsFolder, string dbContext, string outputDirectory) + { + var dbContextOption = string.IsNullOrWhiteSpace(dbContext) + ? string.Empty + : $"--context {dbContext}"; + + var addMigrationCmd = $"dotnet ef migrations add Initial --output-dir {outputDirectory} {dbContextOption}"; + + return CmdHelper.RunCmdAndGetOutput(addMigrationCmd, out int exitCode, dbMigrationsFolder); + } + + private static bool CheckMigrationOutput(string output) + { + return output == null || (output.Contains("Done.") && + output.Contains("To undo this action") && + output.Contains("ef migrations remove")); + } +} \ No newline at end of file From eeb8ae3389bd4c80fa50c3fe4ec0f5c07cfee4fb Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 9 May 2022 11:13:20 +0300 Subject: [PATCH 2/2] Update ProjectCreationCommandBase.cs --- .../Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs index 74a23a6cbc..40d902fb0e 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ProjectCreationCommandBase.cs @@ -369,7 +369,7 @@ public abstract class ProjectCreationCommandBase if (string.IsNullOrWhiteSpace(efCoreProjectPath)) { - Logger.LogWarning("Couldn't find EntityFrameworkCore project to create initial migrations!"); + Logger.LogWarning("Couldn't find the project to create initial migrations!"); return; }