Browse Source

Merge pull request #1089 from abpframework/cli

CLI new project creation
pull/1093/head
Halil İbrahim Kalkan 8 years ago
committed by GitHub
parent
commit
c31d121ae0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json
  2. 24
      framework/Volo.Abp.sln
  3. 27
      framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj
  4. 19
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs
  5. 61
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgs.cs
  6. 104
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs
  7. 35
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineOptions.cs
  8. 7
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs
  9. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs
  10. 51
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs
  11. 22
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CommandSelector.cs
  12. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ICommandSelector.cs
  13. 10
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/IConsoleCommand.cs
  14. 20
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/MainHelpCommand.cs
  15. 91
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewProjectCommand.cs
  16. 45
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/AbpIoTemplateStore.cs
  17. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/DatabaseProvider.cs
  18. 25
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/GithubRepositoryInfo.cs
  19. 37
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildContext.cs
  20. 20
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildContextExtensions.cs
  21. 22
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildPipeline.cs
  22. 21
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildPipelineBuilder.cs
  23. 7
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildPipelineStep.cs
  24. 7
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectResult.cs
  25. 23
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/CreateProjectResultZipStep.cs
  26. 26
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/FileEntryListReadStep.cs
  27. 113
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/NugetReferenceReplaceStep.cs
  28. 71
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/RemoveProjectFromSolutionStep.cs
  29. 116
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/SolutionRenameStep.cs
  30. 80
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs
  31. 19
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/TemplateCodeDeleteStep.cs
  32. 26
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/TemplateInfo.cs
  33. 37
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/DownloadInfo.cs
  34. 126
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntry.cs
  35. 81
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntryExtensions.cs
  36. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntryList.cs
  37. 24
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntryListExtensions.cs
  38. 21
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Github/GithubRelease.cs
  39. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/IProjectBuilder.cs
  40. 11
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ITemplateInfoProvider.cs
  41. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ITemplateStore.cs
  42. 26
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ProjectBuildArgs.cs
  43. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ProjectBuildResult.cs
  44. 62
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ProjectBuilder.cs
  45. 50
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/SolutionName.cs
  46. 12
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/TemplateFile.cs
  47. 30
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/TemplateInfoProvider.cs
  48. 51
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Templates/MvcApplicationTemplate.cs
  49. 18
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Templates/MvcModuleTemplate.cs
  50. 18
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Templates/ServiceTemplate.cs
  51. 57
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Zipping/ZipFileExtensions.cs
  52. 12
      framework/src/Volo.Abp.Cli/Program.cs
  53. 14
      framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj
  54. 14
      framework/src/Volo.Abp.Cli/Volo/Abp/Cli/AbpCliModule.cs
  55. 40
      framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs
  56. 12
      framework/src/Volo.Abp.Core/System/Reflection/AbpAssemblyExtensions.cs
  57. 2
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  58. 4
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs
  59. 15
      framework/test/Volo.Abp.Cli.Tests/Volo.Abp.Cli.Tests.csproj
  60. 7
      framework/test/Volo.Abp.Cli.Tests/Volo/Abp/Cli/AbpCliTestBase.cs
  61. 13
      framework/test/Volo.Abp.Cli.Tests/Volo/Abp/Cli/AbpCliTestModule.cs
  62. 68
      framework/test/Volo.Abp.Cli.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs
  63. 1
      nupkg/common.ps1

1
abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json

@ -3,6 +3,7 @@
"TemplateFiles/": {},
"Logs/": {},
"wwwroot/files/": {},
"wwwroot/downloads/": {},
"appsettings.json": {},
"web.config": {}
}

24
framework/Volo.Abp.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.156
# Visual Studio 15
VisualStudioVersion = 15.0.28307.168
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}"
EndProject
@ -218,14 +218,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp", "src\Volo.Abp\Vo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Authentication.JwtBearer", "src\Volo.Abp.AspNetCore.Authentication.JwtBearer\Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj", "{46C6336C-A1D8-4858-98CE-6F4C698C5A77}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Cli", "src\Volo.Abp.Cli\Volo.Abp.Cli.csproj", "{69168816-4394-4DDA-BB6B-C21983D37F0B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli", "src\Volo.Abp.Cli\Volo.Abp.Cli.csproj", "{69168816-4394-4DDA-BB6B-C21983D37F0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation", "src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj", "{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FluentValidation", "src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj", "{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation.Tests", "test\Volo.Abp.FluentValidation.Tests\Volo.Abp.FluentValidation.Tests.csproj", "{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FluentValidation.Tests", "test\Volo.Abp.FluentValidation.Tests\Volo.Abp.FluentValidation.Tests.csproj", "{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli.Tests", "test\Volo.Abp.Cli.Tests\Volo.Abp.Cli.Tests.csproj", "{92B70EFF-C1B1-4D1D-8BCE-D116908FC6FF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Sqlite", "src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj", "{58CF8957-5045-4F81-884D-72DF48F721CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Cli.Core", "src\Volo.Abp.Cli.Core\Volo.Abp.Cli.Core.csproj", "{3DA9923E-048E-4FE7-9748-3A0194F5D196}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -668,10 +672,18 @@ Global
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.Build.0 = Release|Any CPU
{92B70EFF-C1B1-4D1D-8BCE-D116908FC6FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92B70EFF-C1B1-4D1D-8BCE-D116908FC6FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92B70EFF-C1B1-4D1D-8BCE-D116908FC6FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92B70EFF-C1B1-4D1D-8BCE-D116908FC6FF}.Release|Any CPU.Build.0 = Release|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58CF8957-5045-4F81-884D-72DF48F721CC}.Release|Any CPU.Build.0 = Release|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DA9923E-048E-4FE7-9748-3A0194F5D196}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -786,7 +798,9 @@ Global
{69168816-4394-4DDA-BB6B-C21983D37F0B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{43D5FE61-ECBF-4B16-AD95-0043E18EB93A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{92B70EFF-C1B1-4D1D-8BCE-D116908FC6FF} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{58CF8957-5045-4F81-884D-72DF48F721CC} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{3DA9923E-048E-4FE7-9748-3A0194F5D196} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

27
framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ionic.Zip" Version="1.9.1.8" />
<PackageReference Include="HtmlAgilityPack.NetCore" Version="1.5.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
<PackageReference Include="System.Security.Permissions" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
</ItemGroup>
</Project>

19
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs

@ -0,0 +1,19 @@
using System.Text;
using Volo.Abp.Domain;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
namespace Volo.Abp.Cli
{
[DependsOn(
typeof(AbpDddDomainModule),
typeof(AbpJsonModule)
)]
public class AbpCliCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
}
}

61
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgs.cs

@ -0,0 +1,61 @@
using System.Linq;
using System.Text;
using JetBrains.Annotations;
namespace Volo.Abp.Cli.Args
{
public class CommandLineArgs
{
[CanBeNull]
public string Command { get; }
[CanBeNull]
public string Target { get; }
[NotNull]
public CommandLineOptions Options { get; }
public CommandLineArgs([CanBeNull] string command = null, [CanBeNull] string target = null)
{
Command = command;
Target = target;
Options = new CommandLineOptions();
}
public static CommandLineArgs Empty()
{
return new CommandLineArgs();
}
public override string ToString()
{
var sb = new StringBuilder();
if (Command != null)
{
sb.AppendLine($"Command: {Command}");
}
if (Target != null)
{
sb.AppendLine($"Target: {Target}");
}
if (Options.Any())
{
sb.AppendLine("Options:");
foreach (var option in Options)
{
sb.AppendLine($" - {option.Key} = {option.Value}");
}
}
if (sb.Length <= 0)
{
sb.Append("<EMPTY>");
}
return sb.ToString();
}
}
}

104
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Args
{
public class CommandLineArgumentParser : ICommandLineArgumentParser, ITransientDependency
{
public CommandLineArgs Parse(string[] args)
{
if (args.IsNullOrEmpty())
{
return CommandLineArgs.Empty();
}
var argumentList = args.ToList();
//Get command
var command = argumentList[0];
argumentList.RemoveAt(0);
if (!argumentList.Any())
{
return new CommandLineArgs(command);
}
//Get target
var target = argumentList[0];
if (target.StartsWith("-"))
{
target = null;
}
else
{
argumentList.RemoveAt(0);
}
if (!argumentList.Any())
{
return new CommandLineArgs(command, target);
}
//Get options
var commandLineArgs = new CommandLineArgs(command, target);
while (argumentList.Any())
{
var optionName = ParseOptionName(argumentList[0]);
argumentList.RemoveAt(0);
if (!argumentList.Any())
{
commandLineArgs.Options[optionName] = null;
break;
}
if (IsOptionName(argumentList[0]))
{
commandLineArgs.Options[optionName] = null;
continue;
}
commandLineArgs.Options[optionName] = argumentList[0];
argumentList.RemoveAt(0);
}
return commandLineArgs;
}
private static bool IsOptionName(string argument)
{
return argument.StartsWith("-") || argument.StartsWith("--");
}
private static string ParseOptionName(string argument)
{
if (argument.StartsWith("--"))
{
if (argument.Length <= 2)
{
throw new ArgumentException("Should specify an option name after '--' prefix!");
}
return argument.RemovePreFix("--");
}
if (argument.StartsWith("-"))
{
if (argument.Length <= 1)
{
throw new ArgumentException("Should specify an option name after '--' prefix!");
}
return argument.RemovePreFix("-");
}
throw new ArgumentException("Option names should start with '-' or '--'.");
}
}
}

35
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineOptions.cs

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Cli.Args
{
public class CommandLineOptions : Dictionary<string, string>
{
[CanBeNull]
public string GetOrNull([NotNull] string name, params string[] alternativeNames)
{
Check.NotNullOrWhiteSpace(name, nameof(name));
var value = this.GetOrDefault(name);
if (!value.IsNullOrWhiteSpace())
{
return value;
}
if (!alternativeNames.IsNullOrEmpty())
{
foreach (var alternativeName in alternativeNames)
{
value = this.GetOrDefault(alternativeName);
if (!value.IsNullOrWhiteSpace())
{
return value;
}
}
}
return null;
}
}
}

7
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Cli.Args
{
public interface ICommandLineArgumentParser
{
CommandLineArgs Parse(string[] args);
}
}

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs

@ -0,0 +1,13 @@
using System;
using System.IO;
namespace Volo.Abp.Cli
{
public static class CliPaths
{
public static string TemplateCachePath => Path.Combine(AbpRootPath, "templates");
public static string CliLogPath => Path.Combine(AbpRootPath, "cli", "logs");
private static readonly string AbpRootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".abp");
}
}

51
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs

@ -0,0 +1,51 @@
using System;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Commands;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ProjectBuilding;
namespace Volo.Abp.Cli
{
public class CliService : ITransientDependency
{
public static string Version => typeof(AbpCliCoreModule).Assembly.GetFileVersion();
public ILogger<CliService> Logger { get; set; }
protected ICommandLineArgumentParser CommandLineArgumentParser { get; }
protected ICommandSelector CommandSelector { get; }
public IHybridServiceScopeFactory ServiceScopeFactory { get; }
public CliService(
ICommandLineArgumentParser commandLineArgumentParser,
ICommandSelector commandSelector,
IHybridServiceScopeFactory serviceScopeFactory)
{
CommandLineArgumentParser = commandLineArgumentParser;
CommandSelector = commandSelector;
ServiceScopeFactory = serviceScopeFactory;
Logger = NullLogger<CliService>.Instance;
}
public async Task RunAsync(string[] args)
{
Logger.LogInformation("ABP CLI (abp.io)");
Logger.LogInformation("Version: " + Version);
var commandLineArgs = CommandLineArgumentParser.Parse(args);
var commandType = CommandSelector.Select(commandLineArgs);
using (var scope = ServiceScopeFactory.CreateScope())
{
var command = (IConsoleCommand)scope.ServiceProvider.GetRequiredService(commandType);
await command.ExecuteAsync(commandLineArgs);
}
}
}
}

22
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CommandSelector.cs

@ -0,0 +1,22 @@
using System;
using Volo.Abp.Cli.Args;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public class CommandSelector : ICommandSelector, ITransientDependency
{
public Type Select(CommandLineArgs commandLineArgs)
{
//TODO: Create options to define commands
//TODO: Get from dependency injection instead of new?
if (commandLineArgs.Command == "new")
{
return typeof(NewProjectCommand);
}
return typeof(MainHelpCommand);
}
}
}

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ICommandSelector.cs

@ -0,0 +1,10 @@
using System;
using Volo.Abp.Cli.Args;
namespace Volo.Abp.Cli.Commands
{
public interface ICommandSelector
{
Type Select(CommandLineArgs commandLineArgs);
}
}

10
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/IConsoleCommand.cs

@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Volo.Abp.Cli.Args;
namespace Volo.Abp.Cli.Commands
{
public interface IConsoleCommand
{
Task ExecuteAsync(CommandLineArgs commandLineArgs);
}
}

20
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/MainHelpCommand.cs

@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Cli.Args;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public class MainHelpCommand : IConsoleCommand, ITransientDependency
{
public Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
Console.WriteLine("*********** ABP CLI ****************");
Console.WriteLine();
Console.WriteLine("Usage:");
Console.WriteLine();
Console.WriteLine(" abp <command> <target> [options]");
return Task.CompletedTask;
}
}
}

91
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewProjectCommand.cs

@ -0,0 +1,91 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Ionic.Zip;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ProjectBuilding;
using Volo.Abp.ProjectBuilding.Building;
namespace Volo.Abp.Cli.Commands
{
public class NewProjectCommand : IConsoleCommand, ITransientDependency
{
public ILogger<NewProjectCommand> Logger { get; set; }
protected ProjectBuilder ProjectBuilder { get; }
public NewProjectCommand(ProjectBuilder projectBuilder)
{
ProjectBuilder = projectBuilder;
Logger = NullLogger<NewProjectCommand>.Instance;
}
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
if (commandLineArgs.Target == null)
{
Logger.LogWarning("Project name is missing.");
Logger.LogWarning("Usage:");
Logger.LogWarning(" abp new <project-name> [-t <template-name>]");
Logger.LogWarning("Examples:");
Logger.LogWarning(" abp new Acme.BookStore");
Logger.LogWarning(" abp new Acme.BookStore mvc");
return;
}
Logger.LogInformation("Creating a new project...");
Logger.LogInformation("Project name: " + commandLineArgs.Target);
var result = await ProjectBuilder.BuildAsync(
new ProjectBuildArgs(
SolutionName.Parse(commandLineArgs.Target),
GetDatabaseProviderOrNull(commandLineArgs),
commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long)
)
);
using (var templateFileStream = new MemoryStream(result.ZipContent))
{
using (var templateZipFile = ZipFile.Read(templateFileStream))
{
templateZipFile.ExtractAll(Directory.GetCurrentDirectory(), ExtractExistingFileAction.Throw);
}
}
Logger.LogInformation($"Successfully created the project '{commandLineArgs.Target}'");
}
protected virtual DatabaseProvider GetDatabaseProviderOrNull(CommandLineArgs commandLineArgs)
{
var optionValue = commandLineArgs.Options.GetOrNull(Options.DatabaseProvider.Short, Options.DatabaseProvider.Long);
switch (optionValue)
{
case "ef":
return DatabaseProvider.EntityFrameworkCore;
case "mongodb":
return DatabaseProvider.MongoDb;
default:
return DatabaseProvider.NotSpecified;
}
}
public static class Options
{
public static class Template
{
public const string Short = "t";
public const string Long = "template";
}
public static class DatabaseProvider
{
public const string Short = "d";
public const string Long = "databaseProvider";
}
}
}
}

45
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/AbpIoTemplateStore.cs

@ -0,0 +1,45 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli;
using Volo.Abp.DependencyInjection;
using Volo.Abp.IO;
namespace Volo.Abp.ProjectBuilding
{
public class AbpIoTemplateStore : ITemplateStore, ITransientDependency
{
public ILogger<AbpIoTemplateStore> Logger { get; set; }
public AbpIoTemplateStore()
{
Logger = NullLogger<AbpIoTemplateStore>.Instance;
}
public async Task<TemplateFile> GetAsync(string templateName, string version)
{
var localCacheFolder = Path.Combine(CliPaths.TemplateCachePath, version);
DirectoryHelper.CreateIfNotExists(localCacheFolder);
var localCacheFile = Path.Combine(localCacheFolder, templateName + ".zip");
if (File.Exists(localCacheFile))
{
Logger.LogInformation("Using cached template: " + templateName + ", version: " + version);
return new TemplateFile(File.ReadAllBytes(localCacheFile));
}
Logger.LogInformation("Downloading template: " + templateName + ", version: " + version);
using (var client = new System.Net.Http.HttpClient())
{
client.Timeout = TimeSpan.FromMinutes(5);
var downloadUrl = "https://abp.io/downloads/templates/" + version + "/" + templateName + ".zip";
var fileContents = await client.GetByteArrayAsync(downloadUrl);
File.WriteAllBytes(localCacheFile, fileContents);
return new TemplateFile(fileContents);
}
}
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/DatabaseProvider.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.ProjectBuilding.Building
{
public enum DatabaseProvider
{
NotSpecified = 0,
EntityFrameworkCore = 1,
MongoDb = 2
}
}

25
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/GithubRepositoryInfo.cs

@ -0,0 +1,25 @@
using System;
namespace Volo.Abp.ProjectBuilding.Building
{
public class GithubRepositoryInfo
{
public string RepositoryNameWithOrganization { get; }
public string RepositoryName { get; }
public string AccessToken { get; }
public GithubRepositoryInfo(string repositoryNameWithOrganization, string accessToken)
{
if (!repositoryNameWithOrganization.Contains("/"))
{
throw new ApplicationException($"{nameof(repositoryNameWithOrganization)} '{repositoryNameWithOrganization}' is not valid! It should be formatted as 'organization-name/repository-name'." );
}
RepositoryNameWithOrganization = repositoryNameWithOrganization;
RepositoryName = repositoryNameWithOrganization.Split('/')[1];
AccessToken = accessToken;
}
}
}

37
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildContext.cs

@ -0,0 +1,37 @@
using JetBrains.Annotations;
using Volo.Abp.ProjectBuilding.Files;
namespace Volo.Abp.ProjectBuilding.Building
{
public class ProjectBuildContext
{
[NotNull]
public TemplateFile TemplateFile { get; }
[NotNull]
public string Version { get; }
[NotNull]
public ProjectBuildArgs BuildArgs { get; }
[NotNull]
public TemplateInfo Template { get; }
public FileEntryList Files { get; set; }
public ProjectResult Result { get; set; }
public ProjectBuildContext([NotNull] TemplateInfo template,
[NotNull] TemplateFile templateFile,
[NotNull] ProjectBuildArgs buildArgs,
[NotNull] string version)
{
Template = Check.NotNull(template, nameof(template));
TemplateFile = Check.NotNull(templateFile, nameof(templateFile));
BuildArgs = Check.NotNull(buildArgs, nameof(buildArgs));
Version = Check.NotNullOrWhiteSpace(version, nameof(version));
Result = new ProjectResult();
}
}
}

20
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildContextExtensions.cs

@ -0,0 +1,20 @@
using System;
using System.Linq;
using Volo.Abp.ProjectBuilding.Files;
namespace Volo.Abp.ProjectBuilding.Building
{
public static class ProjectBuildContextExtensions
{
public static FileEntry GetFile(this ProjectBuildContext context, string filePath)
{
var file = context.Files.FirstOrDefault(f => f.Name == filePath);
if (file == null)
{
throw new ApplicationException("Could not find file: " + filePath);
}
return file;
}
}
}

22
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildPipeline.cs

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace Volo.Abp.ProjectBuilding.Building
{
public class ProjectBuildPipeline
{
public List<ProjectBuildPipelineStep> Steps { get; }
public ProjectBuildPipeline()
{
Steps = new List<ProjectBuildPipelineStep>();
}
public void Execute(ProjectBuildContext context)
{
foreach (var step in Steps)
{
step.Execute(context);
}
}
}
}

21
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildPipelineBuilder.cs

@ -0,0 +1,21 @@
using Volo.Abp.ProjectBuilding.Building.Steps;
namespace Volo.Abp.ProjectBuilding.Building
{
public static class ProjectBuildPipelineBuilder
{
public static ProjectBuildPipeline Build(ProjectBuildContext context)
{
var pipeline = new ProjectBuildPipeline();
pipeline.Steps.Add(new FileEntryListReadStep());
pipeline.Steps.AddRange(context.Template.GetCustomSteps(context));
pipeline.Steps.Add(new NugetReferenceReplaceStep());
pipeline.Steps.Add(new TemplateCodeDeleteStep());
pipeline.Steps.Add(new SolutionRenameStep());
pipeline.Steps.Add(new CreateProjectResultZipStep());
return pipeline;
}
}
}

7
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectBuildPipelineStep.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.ProjectBuilding.Building
{
public abstract class ProjectBuildPipelineStep
{
public abstract void Execute(ProjectBuildContext context);
}
}

7
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/ProjectResult.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.ProjectBuilding.Building
{
public class ProjectResult
{
public byte[] ZipContent { get; set; }
}
}

23
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/CreateProjectResultZipStep.cs

@ -0,0 +1,23 @@
using Ionic.Zip;
using Volo.Abp.ProjectBuilding.Files;
using Volo.Abp.ProjectBuilding.Zipping;
namespace Volo.Abp.ProjectBuilding.Building.Steps
{
public class CreateProjectResultZipStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
context.Result.ZipContent = CreateZipFileFromEntries(context.Files);
}
private static byte[] CreateZipFileFromEntries(FileEntryList entries)
{
using (var resultZipFile = new ZipFile())
{
entries.CopyToZipFile(resultZipFile);
return resultZipFile.GetBytes();
}
}
}
}

26
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/FileEntryListReadStep.cs

@ -0,0 +1,26 @@
using System.IO;
using Ionic.Zip;
using Volo.Abp.ProjectBuilding.Files;
using Volo.Abp.ProjectBuilding.Zipping;
namespace Volo.Abp.ProjectBuilding.Building.Steps
{
public class FileEntryListReadStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
context.Files = GetEntriesFromZipFile(context.TemplateFile.FileBytes);
}
private static FileEntryList GetEntriesFromZipFile(byte[] fileBytes)
{
using (var memoryStream = new MemoryStream(fileBytes))
{
using (var templateZipFile = ZipFile.Read(memoryStream))
{
return templateZipFile.ToFileEntryList();
}
}
}
}
}

113
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/NugetReferenceReplaceStep.cs

@ -0,0 +1,113 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using HtmlAgilityPack;
using Volo.Abp.ProjectBuilding.Files;
namespace Volo.Abp.ProjectBuilding.Building.Steps
{
public class NugetReferenceReplaceStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
new NugetReferenceReplacer(
context.Files,
"MyCompanyName",
"MyProjectName",
context.Version
).Run();
}
private class NugetReferenceReplacer
{
private readonly List<FileEntry> _entries;
private readonly string _companyNamePlaceHolder;
private readonly string _projectNamePlaceHolder;
private readonly string _latestNugetPackageVersion;
public NugetReferenceReplacer(List<FileEntry> entries, string companyNamePlaceHolder, string projectNamePlaceHolder, string latestNugetPackageVersion)
{
_entries = entries;
_companyNamePlaceHolder = companyNamePlaceHolder;
_projectNamePlaceHolder = projectNamePlaceHolder;
_latestNugetPackageVersion = latestNugetPackageVersion;
}
public void Run()
{
foreach (var fileEntry in _entries)
{
if (fileEntry.Name.EndsWith(".csproj"))
{
fileEntry.SetContent(ProcessFileContent(fileEntry.Content));
}
}
}
private string ProcessFileContent(string content)
{
Check.NotNull(content, nameof(content));
var doc = new HtmlDocument();
doc.Load(GenerateStreamFromString(content));
var nodes = doc.DocumentNode.SelectNodes("//projectreference[@include]");
if (nodes == null)
{
return content;
}
return ProcessReferenceNodes(nodes, content);
}
private string ProcessReferenceNodes(HtmlNodeCollection nodes, string content)
{
Check.NotNull(nodes, nameof(nodes));
Check.NotNull(content, nameof(content));
foreach (var node in nodes)
{
var valueAttr = node.Attributes.FirstOrDefault(a => a.Name.ToLower() == "include");
// ReSharper disable once PossibleNullReferenceException : Can not be null because nodes are selected with include attribute filter in previous method
if (valueAttr.Value.Contains($"{_companyNamePlaceHolder}.{_projectNamePlaceHolder}"))
{
continue;
}
var newValue = ConvertToNugetReference(valueAttr.Value);
var oldLine = $"<ProjectReference Include=\"{valueAttr.Value}\"";
var oldLineAlt = $"<ProjectReference Include=\"{valueAttr.Value}\"";
var newLine = $"<PackageReference Include=\"{newValue}\" Version=\"{_latestNugetPackageVersion}\"";
content = content.Replace(oldLine, newLine);
content = content.Replace(oldLineAlt, newLine);
}
return content;
}
private string ConvertToNugetReference(string oldValue)
{
var directory = new DirectoryInfo(oldValue);
var newValue = directory.Name.Replace(".csproj", "");
return newValue;
}
private static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
}
}

71
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/RemoveProjectFromSolutionStep.cs

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.ProjectBuilding.Building.Steps
{
public class RemoveProjectFromSolutionStep : ProjectBuildPipelineStep
{
private readonly string _projectName;
private readonly string _solutionFilePath;
private readonly string _projectFolderPath;
public RemoveProjectFromSolutionStep(
string projectName,
string solutionFilePath = null,
string projectFolderPath = null)
{
_projectName = projectName;
_solutionFilePath = solutionFilePath ?? "/MyCompanyName.MyProjectName.sln";
_projectFolderPath = projectFolderPath ?? ("/src/" + projectName);
}
public override void Execute(ProjectBuildContext context)
{
context.Files.RemoveAll(file => file.Name.StartsWith(_projectFolderPath));
var solutionFile = context.GetFile(_solutionFilePath);
solutionFile.NormalizeLineEndings();
solutionFile.SetLines(RemoveProject(solutionFile.GetLines().ToList()));
}
private List<string> RemoveProject(List<string> solutionFileLines)
{
var projectKey = FindProjectKey(solutionFileLines);
var newSolutionFileLines = new List<string>();
var firstOccurence = true;
for (var i = 0; i < solutionFileLines.Count; ++i)
{
if (solutionFileLines[i].Contains(projectKey))
{
if (firstOccurence)
{
firstOccurence = false;
++i; //Skip "EndProject" line too.
}
continue;
}
newSolutionFileLines.Add(solutionFileLines[i]);
}
return newSolutionFileLines;
}
private string FindProjectKey(List<string> solutionFileLines)
{
foreach (var solutionFileLine in solutionFileLines)
{
if (solutionFileLine.Contains(_projectName))
{
var curlyBracketStartIndex = solutionFileLine.LastIndexOf("{", StringComparison.OrdinalIgnoreCase);
var curlyBracketEndIndex = solutionFileLine.LastIndexOf("}", StringComparison.OrdinalIgnoreCase);
return solutionFileLine.Substring(curlyBracketStartIndex + 1, curlyBracketEndIndex - curlyBracketStartIndex - 1);
}
}
throw new ApplicationException($"The solution file '{_solutionFilePath}' does not contain a project '{_projectName}'");
}
}
}

116
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/SolutionRenameStep.cs

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.ProjectBuilding.Files;
namespace Volo.Abp.ProjectBuilding.Building.Steps
{
public class SolutionRenameStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
new SolutionRenamer(
context.Files,
"MyCompanyName",
"MyProjectName",
context.BuildArgs.SolutionName.CompanyName,
context.BuildArgs.SolutionName.ProjectName
).Run();
}
private class SolutionRenamer
{
private readonly List<FileEntry> _entries;
private readonly string _companyNamePlaceHolder;
private readonly string _projectNamePlaceHolder;
private readonly string _companyName;
private readonly string _projectName;
public SolutionRenamer(List<FileEntry> entries, string companyNamePlaceHolder, string projectNamePlaceHolder, string companyName, string projectName)
{
if (string.IsNullOrWhiteSpace(companyName))
{
companyName = null;
}
if (companyNamePlaceHolder == null && companyName != null)
{
throw new UserFriendlyException($"Can not set {nameof(companyName)} if {nameof(companyNamePlaceHolder)} is null.");
}
_entries = entries;
_companyNamePlaceHolder = companyNamePlaceHolder;
_projectNamePlaceHolder = projectNamePlaceHolder ?? throw new ArgumentNullException(nameof(projectNamePlaceHolder));
_companyName = companyName;
_projectName = projectName ?? throw new ArgumentNullException(nameof(projectName));
}
public void Run()
{
if (_companyNamePlaceHolder != null && _companyName != null)
{
RenameDirectoryRecursively(_companyNamePlaceHolder, _companyName);
RenameAllFiles(_companyNamePlaceHolder, _companyName);
ReplaceContent(_companyNamePlaceHolder, _companyName);
}
else if (_companyNamePlaceHolder != null)
{
RenameDirectoryRecursively(_companyNamePlaceHolder + "." + _projectNamePlaceHolder, _projectNamePlaceHolder);
RenameAllFiles(_companyNamePlaceHolder + "." + _projectNamePlaceHolder, _projectNamePlaceHolder);
ReplaceContent(_companyNamePlaceHolder + "." + _projectNamePlaceHolder, _projectNamePlaceHolder);
}
RenameDirectoryRecursively(_projectNamePlaceHolder, _projectName);
RenameAllFiles(_projectNamePlaceHolder, _projectName);
ReplaceContent(_projectNamePlaceHolder, _projectName);
}
private void RenameDirectoryRecursively(string placeHolder, string name)
{
foreach (var entry in _entries.Where(e => e.IsDirectory))
{
if (entry.Name.Contains(placeHolder))
{
entry.SetName(entry.Name.Replace(placeHolder, name));
}
}
}
private void RenameAllFiles(string placeHolder, string name)
{
foreach (var entry in _entries.Where(e => !e.IsDirectory))
{
if (entry.Name.Contains(placeHolder))
{
entry.SetName(entry.Name.Replace(placeHolder, name));
}
}
}
private void ReplaceContent(string placeHolder, string name)
{
foreach (var entry in _entries.Where(e => !e.IsDirectory))
{
if (entry.Content.Length < placeHolder.Length)
{
continue;
}
if (entry.IsBinaryFile)
{
continue;
}
var newContent = entry.Content.Replace(placeHolder, name);
if (newContent != entry.Content)
{
entry.SetContent(newContent);
}
}
}
}
}
}

80
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs

@ -0,0 +1,80 @@
using System;
using Volo.Abp.ProjectBuilding.Files;
namespace Volo.Abp.ProjectBuilding.Building.Steps
{
public class SwitchEntityFrameworkCoreToMongoDbStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
ChangeProjectReference(context);
ChangeWebModuleUsage(context);
ChangeConnectionString(context);
}
private void ChangeProjectReference(ProjectBuildContext context)
{
var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj");
file.NormalizeLineEndings();
var lines = file.GetLines();
for (var i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("ProjectReference") && lines[i].Contains("MyCompanyName.MyProjectName.EntityFrameworkCore"))
{
lines[i] = lines[i].Replace("EntityFrameworkCore", "MongoDB");
file.SetLines(lines);
return;
}
}
throw new ApplicationException("Could not find the EntityFrameworkCore reference in the MyCompanyName.MyProjectName.Web.csproj!");
}
private void ChangeWebModuleUsage(ProjectBuildContext context)
{
var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs");
file.NormalizeLineEndings();
file.RemoveTemplateCodeIfNot("EntityFrameworkCore");
var lines = file.GetLines();
for (var i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("using MyCompanyName.MyProjectName.EntityFrameworkCore"))
{
lines[i] = "using MyCompanyName.MyProjectName.MongoDb;";
}
else if (lines[i].Contains("MyProjectNameEntityFrameworkCoreModule"))
{
lines[i] = lines[i].Replace("MyProjectNameEntityFrameworkCoreModule", "MyProjectNameMongoDbModule");
}
}
file.SetLines(lines);
}
private void ChangeConnectionString(ProjectBuildContext context)
{
var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/appsettings.json");
file.NormalizeLineEndings();
var lines = file.GetLines();
for (var i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("Default") && lines[i].Contains("Database"))
{
lines[i] = " \"Default\": \"mongodb://localhost:27017|MyProjectName\"";
file.SetLines(lines);
return;
}
}
throw new ApplicationException("Could not find the 'Default' connection string in appsettings.json file!");
}
}
}

19
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/Steps/TemplateCodeDeleteStep.cs

@ -0,0 +1,19 @@
using Volo.Abp.ProjectBuilding.Files;
namespace Volo.Abp.ProjectBuilding.Building.Steps
{
public class TemplateCodeDeleteStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
foreach (var file in context.Files)
{
if (file.Name.EndsWith(".cs")) //TODO: Why only cs!
{
file.RemoveTemplateCode();
file.RemoveTemplateCodeMarkers();
}
}
}
}
}

26
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Building/TemplateInfo.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.ProjectBuilding.Building
{
public abstract class TemplateInfo
{
public string Name { get; }
public DatabaseProvider DefaultDatabaseProvider { get; }
protected TemplateInfo(
[NotNull] string name,
DatabaseProvider defaultDatabaseProvider = DatabaseProvider.NotSpecified)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
DefaultDatabaseProvider = Check.NotNull(defaultDatabaseProvider, nameof(defaultDatabaseProvider));
}
public virtual IEnumerable<ProjectBuildPipelineStep> GetCustomSteps(ProjectBuildContext context)
{
return Array.Empty<ProjectBuildPipelineStep>();
}
}
}

37
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/DownloadInfo.cs

@ -0,0 +1,37 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.ProjectBuilding.Building;
namespace Volo.Abp.ProjectBuilding
{
[Table("Downloads")]
public class DownloadInfo : CreationAuditedAggregateRoot<int>
{
[Required]
[StringLength(128)]
public string ProjectName { get; set; }
[Required]
[StringLength(42)]
public string TemplateName { get; set; }
[Required]
public DatabaseProvider DatabaseProvider { get; set; }
[Required]
[StringLength(20)]
public string Version { get; set; }
public int CreationDuration { get; set; }
public DownloadInfo(string projectName, string templateName, DatabaseProvider databaseProvider, string version, int creationDuration)
{
ProjectName = projectName;
TemplateName = templateName;
DatabaseProvider = databaseProvider;
Version = version;
CreationDuration = creationDuration;
}
}
}

126
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntry.cs

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Volo.Abp.ProjectBuilding.Files
{
public class FileEntry
{
private static string[] BinaryFileExtensions = {
".exe", ".dll", ".bin",
".suo", ".obj", ".pdb",
".png", "jpg", "jpeg", ".ico"
,".woff", ".woff2", ".eot", ".svg", ".ttf"
};
public string Name { get; private set; }
public bool IsDirectory { get; }
public Encoding Encoding { get; }
public byte[] Bytes { get; private set; }
public string Content { get; private set; }
public bool IsBinaryFile { get; private set; }
public FileEntry(string name, byte[] bytes, bool isDirectory)
{
Name = name;
Bytes = bytes;
IsDirectory = isDirectory;
Encoding = CalculateEncoding();
Content = Encoding.GetString(bytes);
IsBinaryFile = CalculateIsBinaryFile();
}
public void SetContent(string fileContent)
{
Content = fileContent;
Bytes = Encoding.GetBytes(fileContent);
}
public string[] GetLines()
{
return Content.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None);
}
public void SetLines(IEnumerable<string> lines)
{
SetContent(lines.JoinAsString(Environment.NewLine));
}
public void SetName(string fileName)
{
Name = fileName;
IsBinaryFile = CalculateIsBinaryFile();
}
public void NormalizeLineEndings()
{
if (Content.IsNullOrEmpty())
{
return;
}
SetContent(Content.NormalizeLineEndings());
}
public override string ToString()
{
var str = new StringBuilder(Name);
if (IsDirectory)
{
str.Append(" [DIR]");
}
str.Append($" [{Bytes.Length}]");
return str.ToString();
}
private Encoding CalculateEncoding()
{
if (Bytes.IsNullOrEmpty())
{
return Encoding.ASCII;
}
if (Bytes[0] == 0x2b && Bytes[1] == 0x2f && Bytes[2] == 0x76)
{
return Encoding.UTF7;
}
if (Bytes[0] == 0xef && Bytes[1] == 0xbb && Bytes[2] == 0xbf)
{
return Encoding.UTF8;
}
if (Bytes[0] == 0xff && Bytes[1] == 0xfe)
{
return Encoding.Unicode; //UTF-16LE
}
if (Bytes[0] == 0xfe && Bytes[1] == 0xff)
{
return Encoding.BigEndianUnicode; //UTF-16BE
}
if (Bytes[0] == 0 && Bytes[1] == 0 && Bytes[2] == 0xfe && Bytes[3] == 0xff)
{
return Encoding.UTF32;
}
return Encoding.UTF8;
}
private bool CalculateIsBinaryFile()
{
return BinaryFileExtensions.Any(ext => Name.EndsWith(ext));
}
}
}

81
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntryExtensions.cs

@ -0,0 +1,81 @@
using System.Collections.Generic;
namespace Volo.Abp.ProjectBuilding.Files
{
public static class FileEntryExtensions
{
public static void RemoveTemplateCode(this FileEntry file)
{
RemoveMarkedTemplateCode(file, "<TEMPLATE-REMOVE>");
}
public static void RemoveTemplateCodeIf(this FileEntry file, string condition)
{
RemoveByCondition(file, "IF", condition);
}
public static void RemoveTemplateCodeIfNot(this FileEntry file, string condition)
{
RemoveByCondition(file, "IF-NOT", condition);
}
public static void RemoveTemplateCodeMarkers(this FileEntry file)
{
file.NormalizeLineEndings();
var lines = file.GetLines();
var newLines = new List<string>();
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("<TEMPLATE-REMOVE") ||
lines[i].Contains("</TEMPLATE-REMOVE>"))
{
//TODO: What if we use inline like: <TEMPLATE-REMOVE IF-NOT="..."> some-code </TEMPLATE-REMOVE>
//TODO: This logic skips the code in that case. Should handle it
++i;
}
if (i < lines.Length)
{
newLines.Add(lines[i]);
}
}
file.SetLines(newLines);
}
private static void RemoveByCondition(this FileEntry file, string conditionName, string condition)
{
RemoveMarkedTemplateCode(file, $"<TEMPLATE-REMOVE {conditionName}='{condition}'>");
}
private static void RemoveMarkedTemplateCode(this FileEntry file, string beginMark)
{
file.NormalizeLineEndings();
var lines = file.GetLines();
var newLines = new List<string>();
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains(beginMark))
{
while (!lines[i].Contains("</TEMPLATE-REMOVE>") && i < lines.Length)
{
++i;
}
++i;
}
if (i < lines.Length)
{
newLines.Add(lines[i]);
}
}
file.SetLines(newLines);
}
}
}

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntryList.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Volo.Abp.ProjectBuilding.Files
{
public class FileEntryList : List<FileEntry>
{
public FileEntryList(IEnumerable<FileEntry> entries)
: base(entries)
{
}
}
}

24
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Files/FileEntryListExtensions.cs

@ -0,0 +1,24 @@
using System;
using Ionic.Zip;
namespace Volo.Abp.ProjectBuilding.Files
{
public static class FileEntryListExtensions
{
public static void CopyToZipFile(this FileEntryList fileEntryList, ZipFile zipFile)
{
foreach (var entry in fileEntryList)
{
try
{
zipFile.AddEntry(entry.Name, entry.Bytes);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
}

21
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Github/GithubRelease.cs

@ -0,0 +1,21 @@
using System;
using Newtonsoft.Json;
namespace Volo.Abp.ProjectBuilding.Github
{
[JsonObject]
public class GithubRelease
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("prerelease")]
public bool IsPrerelease { get; set; }
[JsonProperty("published_at")]
public DateTime PublishTime { get; set; }
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/IProjectBuilder.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.ProjectBuilding
{
public interface IProjectBuilder
{
Task<ProjectBuildResult> BuildAsync(ProjectBuildArgs args);
}
}

11
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ITemplateInfoProvider.cs

@ -0,0 +1,11 @@
using Volo.Abp.ProjectBuilding.Building;
namespace Volo.Abp.ProjectBuilding
{
public interface ITemplateInfoProvider
{
TemplateInfo GetDefault();
TemplateInfo Get(string name);
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ITemplateStore.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.ProjectBuilding
{
public interface ITemplateStore
{
Task<TemplateFile> GetAsync(string templateInfoName, string version);
}
}

26
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ProjectBuildArgs.cs

@ -0,0 +1,26 @@
using JetBrains.Annotations;
using Volo.Abp.ProjectBuilding.Building;
namespace Volo.Abp.ProjectBuilding
{
public class ProjectBuildArgs
{
[NotNull]
public SolutionName SolutionName { get; }
[CanBeNull]
public string TemplateName { get; set; }
public DatabaseProvider DatabaseProvider { get; set; }
public ProjectBuildArgs(
[NotNull] SolutionName solutionName,
DatabaseProvider databaseProvider = DatabaseProvider.NotSpecified,
[CanBeNull] string templateName = null)
{
DatabaseProvider = databaseProvider;
TemplateName = templateName;
SolutionName = Check.NotNull(solutionName, nameof(solutionName));
}
}
}

15
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ProjectBuildResult.cs

@ -0,0 +1,15 @@
namespace Volo.Abp.ProjectBuilding
{
public class ProjectBuildResult
{
public byte[] ZipContent { get; }
public string ProjectName { get; }
public ProjectBuildResult(byte[] zipContent, string projectName)
{
ZipContent = zipContent;
ProjectName = projectName;
}
}
}

62
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/ProjectBuilder.cs

@ -0,0 +1,62 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Cli;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ProjectBuilding.Building;
namespace Volo.Abp.ProjectBuilding
{
public class ProjectBuilder : IProjectBuilder, ITransientDependency
{
protected ITemplateStore TemplateStore { get; }
protected ITemplateInfoProvider TemplateInfoProvider { get; }
public ProjectBuilder(ITemplateStore templateStore, ITemplateInfoProvider templateInfoProvider)
{
TemplateStore = templateStore;
TemplateInfoProvider = templateInfoProvider;
}
public async Task<ProjectBuildResult> BuildAsync(ProjectBuildArgs args)
{
var templateInfo = GetTemplateInfo(args);
args.TemplateName = templateInfo.Name;
if (args.DatabaseProvider == DatabaseProvider.NotSpecified)
{
if (templateInfo.DefaultDatabaseProvider != DatabaseProvider.NotSpecified)
{
args.DatabaseProvider = templateInfo.DefaultDatabaseProvider;
}
}
var version = CliService.Version;
var templateFile = await TemplateStore.GetAsync(args.TemplateName, version);
var context = new ProjectBuildContext(
templateInfo,
templateFile,
args,
version
);
ProjectBuildPipelineBuilder.Build(context).Execute(context);
return new ProjectBuildResult(context.Result.ZipContent, args.SolutionName.ProjectName);
}
private TemplateInfo GetTemplateInfo(ProjectBuildArgs args)
{
if (args.TemplateName.IsNullOrWhiteSpace())
{
return TemplateInfoProvider.GetDefault();
}
else
{
return TemplateInfoProvider.Get(args.TemplateName);
}
}
}
}

50
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/SolutionName.cs

@ -0,0 +1,50 @@
using System;
namespace Volo.Abp.ProjectBuilding
{
public class SolutionName
{
public string FullName { get; }
public string CompanyName { get; }
public string ProjectName { get; }
private SolutionName(string fullName, string companyName, string projectName)
{
FullName = fullName;
CompanyName = companyName;
ProjectName = projectName;
}
public static SolutionName Parse(string fullName)
{
if (fullName.Length < 1)
{
throw new UserFriendlyException("Solution name is not valid. It should be 1 character length at minimum.");
}
string companyName = null;
var projectName = fullName;
if (fullName.Contains("."))
{
var lastDotIndex = fullName.LastIndexOf(".", StringComparison.OrdinalIgnoreCase);
companyName = fullName.Substring(0, lastDotIndex);
projectName = fullName.Substring(lastDotIndex + 1);
if (companyName.Length < 1)
{
throw new UserFriendlyException("Solution name is not valid. Company name should be 1 character length at minimum.");
}
if (projectName.Length < 1)
{
throw new UserFriendlyException("Solution name is not valid. Project name should be 1 character length at minimum.");
}
}
return new SolutionName(fullName, companyName, projectName);
}
}
}

12
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/TemplateFile.cs

@ -0,0 +1,12 @@
namespace Volo.Abp.ProjectBuilding
{
public class TemplateFile
{
public byte[] FileBytes { get; }
public TemplateFile(byte[] fileBytes)
{
FileBytes = fileBytes;
}
}
}

30
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/TemplateInfoProvider.cs

@ -0,0 +1,30 @@
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ProjectBuilding.Building;
using Volo.Abp.ProjectBuilding.Templates;
namespace Volo.Abp.ProjectBuilding
{
public class TemplateInfoProvider : ITemplateInfoProvider, ITransientDependency
{
public TemplateInfo GetDefault()
{
return Get(MvcApplicationTemplate.TemplateName);
}
public TemplateInfo Get(string name)
{
switch (name)
{
case MvcApplicationTemplate.TemplateName:
return new MvcApplicationTemplate();
case MvcModuleTemplate.TemplateName:
return new MvcModuleTemplate();
case ServiceTemplate.TemplateName:
return new ServiceTemplate();
default:
throw new Exception("There is no template found with given name: " + name);
}
}
}
}

51
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Templates/MvcApplicationTemplate.cs

@ -0,0 +1,51 @@
using System.Collections.Generic;
using Volo.Abp.ProjectBuilding.Building;
using Volo.Abp.ProjectBuilding.Building.Steps;
namespace Volo.Abp.ProjectBuilding.Templates
{
public class MvcApplicationTemplate : TemplateInfo
{
/// <summary>
/// "mvc".
/// </summary>
public const string TemplateName = "mvc";
public MvcApplicationTemplate()
: base(TemplateName, DatabaseProvider.EntityFrameworkCore)
{
}
public override IEnumerable<ProjectBuildPipelineStep> GetCustomSteps(ProjectBuildContext context)
{
var steps = new List<ProjectBuildPipelineStep>();
SwitchDatabaseProvider(context, steps);
RemoveOtherDatabaseProviders(context, steps);
return steps;
}
private static void SwitchDatabaseProvider(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.DatabaseProvider == DatabaseProvider.MongoDb)
{
steps.Add(new SwitchEntityFrameworkCoreToMongoDbStep());
}
}
private static void RemoveOtherDatabaseProviders(ProjectBuildContext context, List<ProjectBuildPipelineStep> steps)
{
if (context.BuildArgs.DatabaseProvider != DatabaseProvider.EntityFrameworkCore)
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Application.Tests", projectFolderPath: "test/MyCompanyName.MyProjectName.Application.Tests"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Tests", projectFolderPath: "test/MyCompanyName.MyProjectName.Web.Tests"));
}
if (context.BuildArgs.DatabaseProvider != DatabaseProvider.MongoDb)
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.MongoDB"));
}
}
}
}

18
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Templates/MvcModuleTemplate.cs

@ -0,0 +1,18 @@
using Volo.Abp.ProjectBuilding.Building;
namespace Volo.Abp.ProjectBuilding.Templates
{
public class MvcModuleTemplate : TemplateInfo
{
/// <summary>
/// "mvcmodule".
/// </summary>
public const string TemplateName = "mvcmodule";
public MvcModuleTemplate()
: base(TemplateName)
{
}
}
}

18
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Templates/ServiceTemplate.cs

@ -0,0 +1,18 @@
using Volo.Abp.ProjectBuilding.Building;
namespace Volo.Abp.ProjectBuilding.Templates
{
public class ServiceTemplate : TemplateInfo
{
/// <summary>
/// "service".
/// </summary>
public const string TemplateName = "service";
public ServiceTemplate()
: base(TemplateName)
{
}
}
}

57
framework/src/Volo.Abp.Cli.Core/Volo/Abp/ProjectBuilding/Zipping/ZipFileExtensions.cs

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Ionic.Zip;
using Volo.Abp.ProjectBuilding.Files;
namespace Volo.Abp.ProjectBuilding.Zipping
{
public static class ZipFileExtensions
{
public static byte[] GetBytes(this ZipFile zipFile)
{
using (var ms = new MemoryStream())
{
zipFile.Save(ms);
return ms.ToArray();
}
}
public static FileEntryList ToFileEntryList(this ZipFile zipFile, string rootFolder = null)
{
var zipEntries = zipFile.Entries;
if (rootFolder != null)
{
zipEntries = zipEntries.Where(entry => entry.FileName.StartsWith(rootFolder)).ToList();
}
var fileEntries = new List<FileEntry>();
foreach (var zipEntry in zipEntries)
{
using (var entryStream = zipEntry.OpenReader())
{
var fileName = zipEntry.FileName;
if (rootFolder != null)
{
fileName = fileName.RemovePreFix(rootFolder);
}
if (fileName.IsNullOrEmpty())
{
continue;
}
fileName = fileName.EnsureStartsWith('/');
fileEntries.Add(new FileEntry(fileName, entryStream.GetAllBytes(), zipEntry.IsDirectory));
}
}
return new FileEntryList(fileEntries);
}
}
}

12
framework/src/Volo.Abp.Cli/Program.cs

@ -1,12 +0,0 @@
using System;
namespace Volo.Abp.Cli
{
public class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Welcome to ABP CLI..! This tool is in development. There is no functionality yet. So, bye bye... ;)");
}
}
}

14
framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj

@ -7,6 +7,20 @@
<TargetFramework>netcoreapp2.2</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>abp</ToolCommandName>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NETStandard.Library" Version="2.0.3" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\Volo.Abp.Cli.Core\Volo.Abp.Cli.Core.csproj" />
</ItemGroup>
</Project>

14
framework/src/Volo.Abp.Cli/Volo/Abp/Cli/AbpCliModule.cs

@ -0,0 +1,14 @@
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.Cli
{
[DependsOn(
typeof(AbpCliCoreModule),
typeof(AbpAutofacModule)
)]
public class AbpCliModule : AbpModule
{
}
}

40
framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs

@ -0,0 +1,40 @@
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Events;
using System.IO;
using Volo.Abp.Threading;
namespace Volo.Abp.Cli
{
public class Program
{
private static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.File(Path.Combine(CliPaths.CliLogPath, "abp-cli-logs.txt"))
.WriteTo.Console()
.CreateLogger();
using (var application = AbpApplicationFactory.Create<AbpCliModule>(
options =>
{
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
}))
{
application.Initialize();
AsyncHelper.RunSync(
() => application.ServiceProvider
.GetRequiredService<CliService>()
.RunAsync(args)
);
application.Shutdown();
}
}
}
}

12
framework/src/Volo.Abp.Core/System/Reflection/AbpAssemblyExtensions.cs

@ -0,0 +1,12 @@
using System.Diagnostics;
namespace System.Reflection
{
public static class AbpAssemblyExtensions
{
public static string GetFileVersion(this Assembly assembly)
{
return FileVersionInfo.GetVersionInfo(assembly.Location).FileVersion;
}
}
}

2
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs

@ -56,7 +56,7 @@ namespace Volo.Abp
public virtual void Dispose()
{
//TODO: Shutdown if not done before?
}
protected virtual void SetServiceProvider(IServiceProvider serviceProvider)

4
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs

@ -42,12 +42,12 @@ namespace Volo.Abp.Modularity
}
}
_logger.LogInformation("Initialized all modules.");
_logger.LogInformation("Initialized all ABP modules.");
}
private void LogListOfModules()
{
_logger.LogInformation("Loaded modules:");
_logger.LogInformation("Loaded ABP modules:");
foreach (var module in _moduleContainer.Modules)
{

15
framework/test/Volo.Abp.Cli.Tests/Volo.Abp.Cli.Tests.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>latest</LangVersion>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Cli\Volo.Abp.Cli.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
</ItemGroup>
</Project>

7
framework/test/Volo.Abp.Cli.Tests/Volo/Abp/Cli/AbpCliTestBase.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Cli
{
public class AbpCliTestBase : AbpIntegratedTest<AbpCliTestModule>
{
}
}

13
framework/test/Volo.Abp.Cli.Tests/Volo/Abp/Cli/AbpCliTestModule.cs

@ -0,0 +1,13 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Cli
{
[DependsOn(
typeof(AbpTestBaseModule),
typeof(AbpCliModule)
)]
public class AbpCliTestModule : AbpModule
{
}
}

68
framework/test/Volo.Abp.Cli.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs

@ -0,0 +1,68 @@
using Shouldly;
using System.Linq;
using Volo.Abp.Cli.Args;
using Xunit;
namespace Volo.Abp.Cli
{
public class CommandLineArgumentParser_Tests : AbpCliTestBase
{
private readonly ICommandLineArgumentParser _commandLineArgumentParser;
public CommandLineArgumentParser_Tests()
{
_commandLineArgumentParser = GetRequiredService<ICommandLineArgumentParser>();
}
[Theory]
[InlineData(null)]
[InlineData(new object[] { new string[0] })]
public void Should_Parse_Empty_Arguments(string[] args)
{
var commandLineArgs = _commandLineArgumentParser.Parse(args);
commandLineArgs.Command.ShouldBeNull();
commandLineArgs.Target.ShouldBeNull();
commandLineArgs.Options.Any().ShouldBeFalse();
}
[Fact]
public void Should_Work_With_Only_Command()
{
var commandLineArgs = _commandLineArgumentParser.Parse(new[] { "mycommand" });
commandLineArgs.Command.ShouldBe("mycommand");
commandLineArgs.Target.ShouldBeNull();
commandLineArgs.Options.Any().ShouldBeFalse();
}
[Fact]
public void Should_Work_With_Command_And_Target()
{
var commandLineArgs = _commandLineArgumentParser.Parse(new[] { "mycommand", "mytarget" });
commandLineArgs.Command.ShouldBe("mycommand");
commandLineArgs.Target.ShouldBe("mytarget");
commandLineArgs.Options.Any().ShouldBeFalse();
}
[Fact]
public void Should_Work_With_Command_And_Target_And_Options()
{
var commandLineArgs = _commandLineArgumentParser.Parse(new[]
{
"mycommand", "mytarget", "-a", "value1", "-b", "--optionC", "value2"
});
commandLineArgs.Command.ShouldBe("mycommand");
commandLineArgs.Target.ShouldBe("mytarget");
commandLineArgs.Options.Any().ShouldBeTrue();
commandLineArgs.Options.ShouldContainKey("a");
commandLineArgs.Options["a"].ShouldBe("value1");
commandLineArgs.Options.ShouldContainKey("b");
commandLineArgs.Options["b"].ShouldBeNull();
commandLineArgs.Options.ShouldContainKey("optionC");
commandLineArgs.Options["optionC"].ShouldBe("value2");
}
}
}

1
nupkg/common.ps1

@ -53,6 +53,7 @@ $projects = (
"framework/src/Volo.Abp.BackgroundWorkers",
"framework/src/Volo.Abp.Caching",
"framework/src/Volo.Abp.Castle.Core",
"framework/src/Volo.Abp.Cli.Core",
"framework/src/Volo.Abp.Cli",
"framework/src/Volo.Abp.Core",
"framework/src/Volo.Abp.Data",

Loading…
Cancel
Save