New project creation for CLI

pull/1089/head
Halil ibrahim Kalkan 6 years ago
parent e45caa5b7f
commit 9cd2f24102

@ -1,9 +1,35 @@
using System.Collections.Generic;
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;
}
}
}

@ -1,43 +1,85 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Ionic.Zip;
using Volo.Abp.Cli.Args;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ProjectBuilding;
using Volo.Abp.SolutionTemplating;
using Volo.Abp.SolutionTemplating.Building;
using Volo.Abp.SolutionTemplating.Zipping;
namespace Volo.Abp.Cli.Commands
{
public class NewProjectCommand : IConsoleCommand, ITransientDependency
{
protected SolutionBuilder SolutionBuilder { get; }
protected ProjectBuilder ProjectBuilder { get; }
public NewProjectCommand(SolutionBuilder solutionBuilder)
public NewProjectCommand(ProjectBuilder projectBuilder)
{
SolutionBuilder = solutionBuilder;
ProjectBuilder = projectBuilder;
}
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
if (commandLineArgs.Target == null)
{
Console.WriteLine("Solution name is missing.");
Console.WriteLine("Project name is missing.");
Console.WriteLine("Usage:");
Console.WriteLine(" abp new <project-name>");
Console.WriteLine("Example:");
Console.WriteLine(" abp new <project-name> [-t <template-name>]");
Console.WriteLine("Examples:");
Console.WriteLine(" abp new Acme.BookStore");
Console.WriteLine(" abp new Acme.BookStore mvc");
return;
}
Console.WriteLine("Creating a new solution");
Console.WriteLine("Solution name: " + commandLineArgs.Target);
//await SolutionBuilder.BuildAsync(
// null,
// commandLineArgs.Target,
// DatabaseProvider.EntityFrameworkCore,
// "...",
// true
//);
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);
}
}
}
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";
}
}
}
}

@ -0,0 +1,16 @@
using System.IO;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.ProjectBuilding
{
public class AbpIoTemplateStore : ITemplateStore, ITransientDependency
{
public async Task<TemplateFile> GetAsync(string templateName, string version)
{
return new TemplateFile(
File.ReadAllBytes("C:\\Temp\\abp-templates\\" + version + "\\" + templateName + ".zip")
);
}
}
}

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

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

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

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

@ -0,0 +1,52 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SolutionTemplating;
using Volo.Abp.SolutionTemplating.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 version = "0.16.0"; //TODO: Should get from somewhere!
var templateInfo = GetTemplateInfo(args);
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);
}
}
}
}

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

@ -0,0 +1,48 @@
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ProjectBuilding.Templates;
using Volo.Abp.SolutionTemplating.Building;
namespace Volo.Abp.ProjectBuilding
{
public class TemplateInfoProvider : ITemplateInfoProvider, ITransientDependency
{
public TemplateInfo GetDefault()
{
return Get(TemplateNames.Mvc);
}
public TemplateInfo Get(string name)
{
switch (name)
{
case TemplateNames.Mvc:
return new MvcApplicationTemplate();
case TemplateNames.MvcModule:
return new MvcModuleTemplate();
case TemplateNames.Service:
return new ServiceTemplate();
default:
throw new Exception("There is no template found with given name: " + name);
}
}
public static class TemplateNames
{
/// <summary>
/// "mvc".
/// </summary>
public const string Mvc = "mvc";
/// <summary>
/// "mvcmodule".
/// </summary>
public const string MvcModule = "mvcmodule";
/// <summary>
/// "service".
/// </summary>
public const string Service = "service";
}
}
}

@ -0,0 +1,41 @@
using System.Collections.Generic;
using Volo.Abp.SolutionTemplating.Building;
using Volo.Abp.SolutionTemplating.Building.Steps;
namespace Volo.Abp.ProjectBuilding.Templates
{
public class MvcApplicationTemplate : TemplateInfo
{
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"));
}
}
}
}

@ -0,0 +1,9 @@
using Volo.Abp.SolutionTemplating.Building;
namespace Volo.Abp.ProjectBuilding.Templates
{
public class MvcModuleTemplate : TemplateInfo
{
}
}

@ -0,0 +1,9 @@
using Volo.Abp.SolutionTemplating.Building;
namespace Volo.Abp.ProjectBuilding.Templates
{
public class ServiceTemplate : TemplateInfo
{
}
}

@ -1,8 +1,8 @@
namespace Volo.Abp.SolutionTemplating.Building
{
public enum DatabaseProvider : byte
public enum DatabaseProvider
{
Irrelevant = 0,
NotSpecified = 0,
EntityFrameworkCore = 1,
MongoDb = 2
}

@ -1,28 +1,37 @@
using JetBrains.Annotations;
using Volo.Abp.ProjectBuilding;
using Volo.Abp.SolutionTemplating.Files;
namespace Volo.Abp.SolutionTemplating.Building
{
public class ProjectBuildContext
{
public ProjectBuildRequest Request { get; }
[NotNull]
public TemplateFile TemplateFile { get; }
public string TemplatesFolder { 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,
ProjectBuildRequest request,
string templatesFolder)
public ProjectBuildContext([NotNull] TemplateInfo template,
[NotNull] TemplateFile templateFile,
[NotNull] ProjectBuildArgs buildArgs,
[NotNull] string version)
{
Template = Check.NotNull(template, nameof(template));
Request = request;
TemplatesFolder = templatesFolder;
TemplateFile = Check.NotNull(templateFile, nameof(templateFile));
BuildArgs = Check.NotNull(buildArgs, nameof(buildArgs));
Version = Check.NotNullOrWhiteSpace(version, nameof(version));
Result = new ProjectResult();
}
}

@ -8,7 +8,6 @@ namespace Volo.Abp.SolutionTemplating.Building
{
var pipeline = new ProjectBuildPipeline();
pipeline.Steps.Add(new GithubDownloadStep());
pipeline.Steps.Add(new FileEntryListReadStep());
pipeline.Steps.AddRange(context.Template.GetCustomSteps(context));
pipeline.Steps.Add(new NugetReferenceReplaceStep());

@ -1,23 +0,0 @@
namespace Volo.Abp.SolutionTemplating.Building
{
public class ProjectBuildRequest
{
public SolutionName SolutionName { get; }
public DatabaseProvider DatabaseProvider { get; }
public string Version { get; }
public bool ReplaceLocalReferencesToNuget { get; set; }
public ProjectBuildRequest(
SolutionName solutionName,
DatabaseProvider databaseProvider,
string version)
{
SolutionName = solutionName;
DatabaseProvider = databaseProvider;
Version = version;
}
}
}

@ -9,25 +9,7 @@ namespace Volo.Abp.SolutionTemplating.Building.Steps
{
public override void Execute(ProjectBuildContext context)
{
if (context.Template.RootPathInZipFile == null)
{
context.Files = GetEntriesFromZipFile(context.Template.FilePath);
return;
}
var entryListCachePath = CreateCachePath(context);
if (File.Exists(entryListCachePath))
{
context.Files = GetEntriesFromZipFile(entryListCachePath);
return;
}
context.Files = GetEntriesFromZipFile(
context.Template.FilePath,
context.Template.RootPathInZipFile);
SaveCachedEntries(entryListCachePath, context.Files);
context.Files = GetEntriesFromZipFile(context.Template.FilePath);
}
private static FileEntryList GetEntriesFromZipFile(string filePath, string rootFolder = null)
@ -40,19 +22,5 @@ namespace Volo.Abp.SolutionTemplating.Building.Steps
}
}
}
private static void SaveCachedEntries(string filePath, FileEntryList entries)
{
using (var resultZipFile = new ZipFile())
{
entries.CopyToZipFile(resultZipFile);
resultZipFile.Save(filePath);
}
}
private string CreateCachePath(ProjectBuildContext context)
{
return context.Template.FilePath.Replace(".zip", "-filtered.zip");
}
}
}

@ -1,33 +0,0 @@
using System;
using System.IO;
using Volo.Abp.SolutionTemplating.Github;
namespace Volo.Abp.SolutionTemplating.Building.Steps
{
public class GithubDownloadStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
var githubManager = new GithubManager();
context.Template.Version = githubManager.GetVersion(context);
context.Template.FilePath = Path.Combine(
context.TemplatesFolder,
$"{context.Template.Name}-{context.Template.Version}.zip"
);
context.Template.RootPathInZipFile = context.Template.GithubRepository.RepositoryName + "-" + context.Template.Version.RemovePreFix("v") + context.Template.RootPathInZipFile;
try
{
githubManager.DownloadIfNotExist(context);
}
catch (Exception e)
{
throw e; //TODO: !!!
//var existingVersion = githubManager.GetExistingVersion(context);
}
}
}
}

@ -14,7 +14,7 @@ namespace Volo.Abp.SolutionTemplating.Building.Steps
context.Files,
"MyCompanyName",
"MyProjectName",
context.Template.Version
context.Version
).Run();
}

@ -13,8 +13,8 @@ namespace Volo.Abp.SolutionTemplating.Building.Steps
context.Files,
"MyCompanyName",
"MyProjectName",
context.Request.SolutionName.CompanyName,
context.Request.SolutionName.ProjectName
context.BuildArgs.SolutionName.CompanyName,
context.BuildArgs.SolutionName.ProjectName
).Run();
}

@ -5,26 +5,8 @@ namespace Volo.Abp.SolutionTemplating.Building
{
public abstract class TemplateInfo
{
public string Name { get; set; }
public GithubRepositoryInfo GithubRepository { get; }
public string FilePath { get; set; }
public string RootPathInZipFile { get; set; }
public string Version { get; set; }
protected TemplateInfo(
string name,
GithubRepositoryInfo githubRepository,
string rootPathInZipFile)
{
Name = name;
GithubRepository = githubRepository;
RootPathInZipFile = rootPathInZipFile;
}
public virtual IEnumerable<ProjectBuildPipelineStep> GetCustomSteps(ProjectBuildContext context)
{
return Array.Empty<ProjectBuildPipelineStep>();

@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Volo.Abp.SolutionTemplating.Building;
using Volo.Abp.Threading;
namespace Volo.Abp.SolutionTemplating.Github
{
public class GithubManager
{
private static readonly object LockObject = new object();
public string GetVersion(ProjectBuildContext context)
{
if (context.Request.Version.StartsWith("Branch:", StringComparison.OrdinalIgnoreCase))
{
//TODO: Should not cache branch files!
return context.Request.Version.Substring("Branch:".Length);
}
var url = "https://api.github.com/repos/" + context.Template.GithubRepository.RepositoryNameWithOrganization + "/releases";
using (var client = new System.Net.Http.HttpClient())
{
var credentials = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:", context.Template.GithubRepository.AccessToken);
credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);
client.Timeout = TimeSpan.FromMinutes(30);
client.DefaultRequestHeaders.UserAgent.ParseAdd("MyAgent/1.0");
var releases = JsonConvert.DeserializeObject<List<GithubRelease>>(client.GetStringAsync(url).Result);
if (context.Request.Version == StandardVersions.LatestUnstable)
{
return releases.FirstOrDefault(r => !r.IsPrerelease)?.Name;
}
return releases.FirstOrDefault()?.Name;
}
}
public void DownloadIfNotExist(ProjectBuildContext context)
{
using (var client = new System.Net.Http.HttpClient())
{
if (File.Exists(context.Template.FilePath))
{
return;
}
lock (LockObject)
{
if (File.Exists(context.Template.FilePath))
{
return;
}
var downloadUrl = "https://github.com/" + context.Template.GithubRepository.RepositoryNameWithOrganization + "/archive/" + context.Template.Version + ".zip";
var credentials = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:", context.Template.GithubRepository.AccessToken);
credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(credentials));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);
client.Timeout = TimeSpan.FromMinutes(30);
var fileContents = AsyncHelper.RunSync(() => client.GetByteArrayAsync(downloadUrl));
File.WriteAllBytes(context.Template.FilePath, fileContents);
}
}
}
//public string GetExistingVersion(ProjectBuildContext context)
//{
// var directoryInfo = new DirectoryInfo(templateRootPath);
// var existingFileName = directoryInfo.GetFiles().FirstOrDefault(f => f.Name.StartsWith(repositoryName))?.Name;
// var latestVersion = "";
// if (existingFileName != null)
// {
// var pFrom = existingFileName.IndexOf(repositoryName + "-", StringComparison.Ordinal) + (repositoryName + "-").Length;
// var pTo = existingFileName.LastIndexOf(".zip", StringComparison.Ordinal);
// latestVersion = existingFileName.Substring(pFrom, pTo - pFrom);
// }
// return latestVersion;
//}
}
}

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

@ -1,64 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.SolutionTemplating.Building;
namespace Volo.Abp.SolutionTemplating
{
public class SolutionBuilder : ITransientDependency
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IRepository<DownloadInfo> _downloadRepository;
public SolutionBuilder(
IHostingEnvironment hostingEnvironment,
IRepository<DownloadInfo> downloadRepository)
{
_hostingEnvironment = hostingEnvironment;
_downloadRepository = downloadRepository;
}
public async Task<SolutionBuildResult> BuildAsync(
TemplateInfo template,
string companyAndProjectName,
DatabaseProvider databaseProvider,
string version,
bool replaceLocalReferencesToNuget)
{
var stopwatch = Stopwatch.StartNew();
var templateRootPath = Path.Combine(_hostingEnvironment.ContentRootPath, "TemplateFiles"); //TODO: To another folder that is not in the deployment folder!
var solutionName = SolutionName.Parse(companyAndProjectName);
var context = new ProjectBuildContext(
template,
new ProjectBuildRequest(
solutionName,
databaseProvider,
version
),
templateRootPath
);
ProjectBuildPipelineBuilder.Build(context).Execute(context);
stopwatch.Stop();
await _downloadRepository.InsertAsync(
new DownloadInfo(
solutionName.FullName,
template.Name,
databaseProvider,
context.Template.Version,
Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds)
)
);
return new SolutionBuildResult(context.Result.ZipContent, solutionName.ProjectName);
}
}
}
Loading…
Cancel
Save