mirror of https://github.com/abpframework/abp.git
15 changed files with 428 additions and 37 deletions
@ -0,0 +1,157 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using ICSharpCode.SharpZipLib.Core; |
||||
|
using ICSharpCode.SharpZipLib.Zip; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.Cli.Args; |
||||
|
using Volo.Abp.Cli.ProjectBuilding; |
||||
|
using Volo.Abp.Cli.ProjectBuilding.Building; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Commands |
||||
|
{ |
||||
|
public class GetSourceCommand : IConsoleCommand, ITransientDependency |
||||
|
{ |
||||
|
public ModuleProjectBuilder ModuleProjectBuilder { get; } |
||||
|
|
||||
|
public ILogger<NewCommand> Logger { get; set; } |
||||
|
|
||||
|
public GetSourceCommand(ModuleProjectBuilder moduleProjectBuilder) |
||||
|
{ |
||||
|
ModuleProjectBuilder = moduleProjectBuilder; |
||||
|
Logger = NullLogger<NewCommand>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async Task ExecuteAsync(CommandLineArgs commandLineArgs) |
||||
|
{ |
||||
|
if (commandLineArgs.Target == null) |
||||
|
{ |
||||
|
throw new CliUsageException( |
||||
|
"Module name is missing!" + |
||||
|
Environment.NewLine + Environment.NewLine + |
||||
|
GetUsageInfo() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Logger.LogInformation("Downloading source code of " + commandLineArgs.Target); |
||||
|
|
||||
|
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); |
||||
|
if (version != null) |
||||
|
{ |
||||
|
Logger.LogInformation("Version: " + version); |
||||
|
} |
||||
|
|
||||
|
var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long); |
||||
|
if (outputFolder != null) |
||||
|
{ |
||||
|
if (!Directory.Exists(outputFolder)) |
||||
|
{ |
||||
|
Directory.CreateDirectory(outputFolder); |
||||
|
} |
||||
|
|
||||
|
outputFolder = Path.GetFullPath(outputFolder); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
outputFolder = Directory.GetCurrentDirectory(); |
||||
|
} |
||||
|
|
||||
|
Logger.LogInformation("Output folder: " + outputFolder); |
||||
|
|
||||
|
commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command); |
||||
|
|
||||
|
var result = await ModuleProjectBuilder.BuildAsync( |
||||
|
new ProjectBuildArgs( |
||||
|
SolutionName.Parse(commandLineArgs.Target), |
||||
|
commandLineArgs.Target, |
||||
|
version, |
||||
|
DatabaseProvider.NotSpecified, |
||||
|
UiFramework.NotSpecified, |
||||
|
commandLineArgs.Options |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
using (var templateFileStream = new MemoryStream(result.ZipContent)) |
||||
|
{ |
||||
|
using (var zipInputStream = new ZipInputStream(templateFileStream)) |
||||
|
{ |
||||
|
var zipEntry = zipInputStream.GetNextEntry(); |
||||
|
while (zipEntry != null) |
||||
|
{ |
||||
|
var fullZipToPath = Path.Combine(outputFolder, zipEntry.Name); |
||||
|
var directoryName = Path.GetDirectoryName(fullZipToPath); |
||||
|
|
||||
|
if (!string.IsNullOrEmpty(directoryName)) |
||||
|
{ |
||||
|
Directory.CreateDirectory(directoryName); |
||||
|
} |
||||
|
|
||||
|
var fileName = Path.GetFileName(fullZipToPath); |
||||
|
if (fileName.Length == 0) |
||||
|
{ |
||||
|
zipEntry = zipInputStream.GetNextEntry(); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var buffer = new byte[4096]; // 4K is optimum
|
||||
|
using (var streamWriter = File.Create(fullZipToPath)) |
||||
|
{ |
||||
|
StreamUtils.Copy(zipInputStream, streamWriter, buffer); |
||||
|
} |
||||
|
|
||||
|
zipEntry = zipInputStream.GetNextEntry(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Logger.LogInformation($"'{commandLineArgs.Target}' has been successfully downloaded to '{outputFolder}'"); |
||||
|
} |
||||
|
|
||||
|
public string GetUsageInfo() |
||||
|
{ |
||||
|
var sb = new StringBuilder(); |
||||
|
|
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Usage:"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine(" abp get-source <module-name> [options]"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Options:"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("-o|--output-folder <output-folder> (default: current folder)"); |
||||
|
sb.AppendLine("-v|--version <version> (default: latest version)"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("Examples:"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine(" abp get-source Volo.Blogging"); |
||||
|
sb.AppendLine(" abp get-source Volo.Blogging -o d:\\my-project"); |
||||
|
sb.AppendLine(""); |
||||
|
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); |
||||
|
|
||||
|
return sb.ToString(); |
||||
|
} |
||||
|
|
||||
|
public string GetShortDescription() |
||||
|
{ |
||||
|
return "Downloads the source code of the specified module."; |
||||
|
} |
||||
|
|
||||
|
public static class Options |
||||
|
{ |
||||
|
public static class OutputFolder |
||||
|
{ |
||||
|
public const string Short = "o"; |
||||
|
public const string Long = "output-folder"; |
||||
|
} |
||||
|
|
||||
|
public static class Version |
||||
|
{ |
||||
|
public const string Short = "v"; |
||||
|
public const string Long = "version"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
namespace Volo.Abp.Cli.ProjectBuilding.Building |
||||
|
{ |
||||
|
public class ModuleInfo |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
public string Namespace { get; set; } |
||||
|
|
||||
|
public string DocumentUrl { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using Volo.Abp.Cli.ProjectBuilding.Building.Steps; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.ProjectBuilding.Building |
||||
|
{ |
||||
|
public static class ModuleProjectBuildPipelineBuilder |
||||
|
{ |
||||
|
public static ProjectBuildPipeline Build(ProjectBuildContext context) |
||||
|
{ |
||||
|
var pipeline = new ProjectBuildPipeline(context); |
||||
|
|
||||
|
pipeline.Steps.Add(new FileEntryListReadStep()); |
||||
|
pipeline.Steps.Add(new NugetReferenceReplaceStep()); |
||||
|
pipeline.Steps.Add(new CreateProjectResultZipStep()); |
||||
|
|
||||
|
return pipeline; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Cli.ProjectBuilding.Building; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.ProjectBuilding |
||||
|
{ |
||||
|
public interface IModuleInfoProvider |
||||
|
{ |
||||
|
Task<ModuleInfo> GetAsync(string name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Json; |
||||
|
using Volo.Abp.Cli.Http; |
||||
|
using Volo.Abp.Cli.ProjectBuilding.Building; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Http; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.ProjectBuilding |
||||
|
{ |
||||
|
public class ModuleInfoProvider: IModuleInfoProvider, ITransientDependency |
||||
|
{ |
||||
|
public IJsonSerializer JsonSerializer { get; } |
||||
|
public ICancellationTokenProvider CancellationTokenProvider { get; } |
||||
|
|
||||
|
public ModuleInfoProvider(IJsonSerializer jsonSerializer, ICancellationTokenProvider cancellationTokenProvider) |
||||
|
{ |
||||
|
JsonSerializer = jsonSerializer; |
||||
|
CancellationTokenProvider = cancellationTokenProvider; |
||||
|
} |
||||
|
|
||||
|
public async Task<ModuleInfo> GetAsync(string name) |
||||
|
{ |
||||
|
var moduleList = await GetModuleListAsync(); |
||||
|
|
||||
|
var module = moduleList.FirstOrDefault(m => m.Name == name); |
||||
|
|
||||
|
if (module == null) |
||||
|
{ |
||||
|
throw new Exception("Module not found!"); |
||||
|
} |
||||
|
|
||||
|
return module; |
||||
|
} |
||||
|
|
||||
|
private async Task<List<ModuleInfo>> GetModuleListAsync() |
||||
|
{ |
||||
|
using (var client = new CliHttpClient()) |
||||
|
{ |
||||
|
var responseMessage = await client.GetAsync( |
||||
|
$"{CliUrls.WwwAbpIo}api/download/modules/", |
||||
|
CancellationTokenProvider.Token |
||||
|
); |
||||
|
|
||||
|
if (!responseMessage.IsSuccessStatusCode) |
||||
|
{ |
||||
|
throw new Exception("Remote server returns error! HTTP status code: " + responseMessage.StatusCode); |
||||
|
} |
||||
|
|
||||
|
var result = await responseMessage.Content.ReadAsStringAsync(); |
||||
|
return JsonSerializer.Deserialize<List<ModuleInfo>>(result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,110 @@ |
|||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Cli.Commands; |
||||
|
using Volo.Abp.Cli.Licensing; |
||||
|
using Volo.Abp.Cli.ProjectBuilding.Analyticses; |
||||
|
using Volo.Abp.Cli.ProjectBuilding.Building; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Json; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.ProjectBuilding |
||||
|
{ |
||||
|
public class ModuleProjectBuilder : IProjectBuilder, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<ModuleProjectBuilder> Logger { get; set; } |
||||
|
|
||||
|
protected ISourceCodeStore SourceCodeStore { get; } |
||||
|
protected IModuleInfoProvider ModuleInfoProvider { get; } |
||||
|
protected ICliAnalyticsCollect CliAnalyticsCollect { get; } |
||||
|
protected CliOptions Options { get; } |
||||
|
protected IJsonSerializer JsonSerializer { get; } |
||||
|
protected IApiKeyService ApiKeyService { get; } |
||||
|
|
||||
|
public ModuleProjectBuilder(ISourceCodeStore sourceCodeStore, |
||||
|
IModuleInfoProvider moduleInfoProvider, |
||||
|
ICliAnalyticsCollect cliAnalyticsCollect, |
||||
|
IOptions<CliOptions> options, |
||||
|
IJsonSerializer jsonSerializer, |
||||
|
IApiKeyService apiKeyService) |
||||
|
{ |
||||
|
SourceCodeStore = sourceCodeStore; |
||||
|
ModuleInfoProvider = moduleInfoProvider; |
||||
|
CliAnalyticsCollect = cliAnalyticsCollect; |
||||
|
Options = options.Value; |
||||
|
JsonSerializer = jsonSerializer; |
||||
|
ApiKeyService = apiKeyService; |
||||
|
|
||||
|
Logger = NullLogger<ModuleProjectBuilder>.Instance; |
||||
|
} |
||||
|
|
||||
|
public async Task<ProjectBuildResult> BuildAsync(ProjectBuildArgs args) |
||||
|
{ |
||||
|
var moduleInfo = await GetModuleInfoAsync(args); |
||||
|
|
||||
|
var templateFile = await SourceCodeStore.GetAsync( |
||||
|
args.TemplateName, |
||||
|
SourceCodeTypes.Module, |
||||
|
args.Version |
||||
|
); |
||||
|
|
||||
|
var apiKeyResult = await ApiKeyService.GetApiKeyOrNullAsync(); |
||||
|
if (apiKeyResult?.ApiKey != null) |
||||
|
{ |
||||
|
args.ExtraProperties["api-key"] = apiKeyResult.ApiKey; |
||||
|
} |
||||
|
|
||||
|
if (apiKeyResult?.LicenseCode != null) |
||||
|
{ |
||||
|
args.ExtraProperties["license-code"] = apiKeyResult.LicenseCode; |
||||
|
} |
||||
|
|
||||
|
var context = new ProjectBuildContext( |
||||
|
null, |
||||
|
moduleInfo, |
||||
|
templateFile, |
||||
|
args |
||||
|
); |
||||
|
|
||||
|
ModuleProjectBuildPipelineBuilder.Build(context).Execute(); |
||||
|
|
||||
|
if (!moduleInfo.DocumentUrl.IsNullOrEmpty()) |
||||
|
{ |
||||
|
Logger.LogInformation("Check out the documents at " + moduleInfo.DocumentUrl); |
||||
|
} |
||||
|
|
||||
|
// Exclude unwanted or known options.
|
||||
|
var options = args.ExtraProperties |
||||
|
.Where(x => !x.Key.Equals(CliConsts.Command, StringComparison.InvariantCultureIgnoreCase)) |
||||
|
.Where(x => !x.Key.Equals(NewCommand.Options.OutputFolder.Long, StringComparison.InvariantCultureIgnoreCase) && |
||||
|
!x.Key.Equals(NewCommand.Options.OutputFolder.Short, StringComparison.InvariantCultureIgnoreCase)) |
||||
|
.Where(x => !x.Key.Equals(NewCommand.Options.Version.Long, StringComparison.InvariantCultureIgnoreCase) && |
||||
|
!x.Key.Equals(NewCommand.Options.Version.Short, StringComparison.InvariantCultureIgnoreCase)) |
||||
|
.Select(x => x.Key).ToList(); |
||||
|
|
||||
|
await CliAnalyticsCollect.CollectAsync(new CliAnalyticsCollectInputDto |
||||
|
{ |
||||
|
Tool = Options.ToolName, |
||||
|
Command = args.ExtraProperties.ContainsKey(CliConsts.Command) ? args.ExtraProperties[CliConsts.Command] : "", |
||||
|
DatabaseProvider = null, |
||||
|
IsTiered = false, |
||||
|
UiFramework = null, |
||||
|
Options = JsonSerializer.Serialize(options), |
||||
|
ProjectName = null, |
||||
|
TemplateName = args.TemplateName, |
||||
|
TemplateVersion = templateFile.Version |
||||
|
}); |
||||
|
|
||||
|
return new ProjectBuildResult(context.Result.ZipContent, args.TemplateName); |
||||
|
} |
||||
|
|
||||
|
private async Task<ModuleInfo> GetModuleInfoAsync(ProjectBuildArgs args) |
||||
|
{ |
||||
|
return await ModuleInfoProvider.GetAsync(args.TemplateName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.ProjectBuilding |
||||
|
{ |
||||
|
public static class SourceCodeTypes |
||||
|
{ |
||||
|
public const string Template = "template"; |
||||
|
|
||||
|
public const string Module = "module"; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue