Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI add-package: source code option #8187

Merged
merged 2 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -196,6 +196,7 @@
"MultipleDBOptionsExplanation": "The framework can work with any data source, while the following providers are officially developed and supported;",
"SelectLanguage": "Select language",
"LatestArticleOnCommunity": "Latest Article on ABP Community",
"Register": "Register"
"Register": "Register",
"IsDownloadable": "Is downloadable"
}
}
Expand Up @@ -35,11 +35,16 @@ public virtual async Task ExecuteAsync(CommandLineArgs commandLineArgs)
}

var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
var withSourceCode =commandLineArgs.Options.ContainsKey(Options.SourceCode.Long);
var addSourceCodeToSolutionFile = withSourceCode && commandLineArgs.Options.ContainsKey("add-to-solution-file");

await ProjectNugetPackageAdder.AddAsync(
GetProjectFile(commandLineArgs),
commandLineArgs.Target,
version
version,
true,
withSourceCode,
addSourceCodeToSolutionFile
);
}

Expand Down Expand Up @@ -128,6 +133,11 @@ public static class Version
public const string Short = "v";
public const string Long = "version";
}

public static class SourceCode
{
public const string Long = "with-source-code";
}
}
}
}
Expand Up @@ -59,7 +59,7 @@ public async Task ExecuteAsync(CommandLineArgs commandLineArgs)

commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command);

await _sourceCodeDownloadService.DownloadAsync(
await _sourceCodeDownloadService.DownloadModuleAsync(
commandLineArgs.Target, outputFolder, version, gitHubAbpLocalRepositoryPath, gitHubVoloLocalRepositoryPath, commandLineArgs.Options);
}

Expand Down
Expand Up @@ -17,15 +17,17 @@ namespace Volo.Abp.Cli.Commands.Services
public class SourceCodeDownloadService : ITransientDependency
{
public ModuleProjectBuilder ModuleProjectBuilder { get; }
public PackageProjectBuilder PackageProjectBuilder { get; }
public ILogger<SourceCodeDownloadService> Logger { get; set; }

public SourceCodeDownloadService(ModuleProjectBuilder moduleProjectBuilder)
public SourceCodeDownloadService(ModuleProjectBuilder moduleProjectBuilder, PackageProjectBuilder packageProjectBuilder)
{
ModuleProjectBuilder = moduleProjectBuilder;
PackageProjectBuilder = packageProjectBuilder;
Logger = NullLogger<SourceCodeDownloadService>.Instance;
}

public async Task DownloadAsync(string moduleName, string outputFolder, string version, string gitHubAbpLocalRepositoryPath, string gitHubVoloLocalRepositoryPath, AbpCommandLineOptions options)
public async Task DownloadModuleAsync(string moduleName, string outputFolder, string version, string gitHubAbpLocalRepositoryPath, string gitHubVoloLocalRepositoryPath, AbpCommandLineOptions options)
{
Logger.LogInformation("Downloading source code of " + moduleName);
Logger.LogInformation("Version: " + version);
Expand Down Expand Up @@ -90,6 +92,62 @@ public async Task DownloadAsync(string moduleName, string outputFolder, string v
Logger.LogInformation($"'{moduleName}' has been successfully downloaded to '{outputFolder}'");
}

public async Task DownloadPackageAsync(string packageName, string outputFolder, string version)
{
Logger.LogInformation("Downloading source code of " + packageName);
Logger.LogInformation("Version: " + version);
Logger.LogInformation("Output folder: " + outputFolder);

var result = await PackageProjectBuilder.BuildAsync(
new ProjectBuildArgs(
SolutionName.Parse(packageName),
packageName,
version
)
);

using (var templateFileStream = new MemoryStream(result.ZipContent))
{
using (var zipInputStream = new ZipInputStream(templateFileStream))
{
var zipEntry = zipInputStream.GetNextEntry();
while (zipEntry != null)
{
if (IsAngularTestFile(zipEntry.Name))
{
zipEntry = zipInputStream.GetNextEntry();
continue;
}

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($"'{packageName}' has been successfully downloaded to '{outputFolder}'");
}

private bool IsAngularTestFile(string zipEntryName)
{
if (string.IsNullOrEmpty(zipEntryName))
Expand Down
Expand Up @@ -159,6 +159,11 @@ private async Task<string> GetLatestSourceCodeVersionAsync(string name, string t

private async Task<string> GetTemplateNugetVersionAsync(string name, string type, string version)
{
if (type != SourceCodeTypes.Template)
{
return null;
}

try
{
var url = $"{CliUrls.WwwAbpIo}api/download/{type}/get-nuget-version/";
Expand Down
@@ -0,0 +1,21 @@
using Volo.Abp.Cli.ProjectBuilding.Building.Steps;
using Volo.Abp.Cli.ProjectBuilding.Templates;

namespace Volo.Abp.Cli.ProjectBuilding.Building
{
public static class PackageProjectBuildPipelineBuilder
{
public static ProjectBuildPipeline Build(ProjectBuildContext context)
{
var pipeline = new ProjectBuildPipeline(context);

pipeline.Steps.Add(new FileEntryListReadStep());
pipeline.Steps.Add(new ProjectReferenceReplaceStep());
pipeline.Steps.Add(new ReplaceCommonPropsStep());
pipeline.Steps.Add(new ReplaceConfigureAwaitPropsStep());
pipeline.Steps.Add(new CreateProjectResultZipStep());

return pipeline;
}
}
}
@@ -1,5 +1,6 @@
using JetBrains.Annotations;
using Volo.Abp.Cli.ProjectBuilding.Files;
using Volo.Abp.Cli.ProjectModification;

namespace Volo.Abp.Cli.ProjectBuilding.Building
{
Expand All @@ -15,22 +16,26 @@ public class ProjectBuildContext

public ModuleInfo Module { get; }

public NugetPackageInfo Package { get; }

public FileEntryList Files { get; set; }

public ProjectResult Result { get; set; }

public ProjectBuildContext(
TemplateInfo template,
ModuleInfo module,
NugetPackageInfo package,
[NotNull] TemplateFile templateFile,
[NotNull] ProjectBuildArgs buildArgs)
{
Template = template;
Module = module;
Package = package;
TemplateFile = Check.NotNull(templateFile, nameof(templateFile));
BuildArgs = Check.NotNull(buildArgs, nameof(buildArgs));

Result = new ProjectResult();
}
}
}
}
@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Volo.Abp.Cli.ProjectModification;

namespace Volo.Abp.Cli.ProjectBuilding
{
public interface INugetPackageInfoProvider
{
Task<NugetPackageInfo> GetAsync(string name);
}
}
Expand Up @@ -67,6 +67,7 @@ public async Task<ProjectBuildResult> BuildAsync(ProjectBuildArgs args)
var context = new ProjectBuildContext(
null,
moduleInfo,
null,
templateFile,
args
);
Expand Down
@@ -0,0 +1,62 @@
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.ProjectModification;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;

namespace Volo.Abp.Cli.ProjectBuilding
{
public class NugetPackageInfoProvider : INugetPackageInfoProvider, ITransientDependency
{
public IJsonSerializer JsonSerializer { get; }
public ICancellationTokenProvider CancellationTokenProvider { get; }
public IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; }

private readonly CliHttpClientFactory _cliHttpClientFactory;

public NugetPackageInfoProvider(
IJsonSerializer jsonSerializer,
ICancellationTokenProvider cancellationTokenProvider,
IRemoteServiceExceptionHandler remoteServiceExceptionHandler,
CliHttpClientFactory cliHttpClientFactory)
{
JsonSerializer = jsonSerializer;
CancellationTokenProvider = cancellationTokenProvider;
RemoteServiceExceptionHandler = remoteServiceExceptionHandler;
_cliHttpClientFactory = cliHttpClientFactory;
}

public async Task<NugetPackageInfo> GetAsync(string name)
{
var packageList = await GetPackageListInternalAsync();

var package = packageList.FirstOrDefault(m => m.Name == name);

if (package == null)
{
throw new Exception("Package is not found or downloadable!");
}

return package;
}

private async Task<List<NugetPackageInfo>> GetPackageListInternalAsync()
{
var client = _cliHttpClientFactory.CreateClient();

using (var responseMessage = await client.GetAsync(
$"{CliUrls.WwwAbpIo}api/download/packages/",
CancellationTokenProvider.Token
))
{
await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(responseMessage);
var result = await responseMessage.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<List<NugetPackageInfo>>(result);
}
}
}
}
@@ -0,0 +1,109 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
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.Cli.ProjectModification;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;

namespace Volo.Abp.Cli.ProjectBuilding
{
public class PackageProjectBuilder : IProjectBuilder, ITransientDependency
{
public ILogger<PackageProjectBuilder> Logger { get; set; }
protected ISourceCodeStore SourceCodeStore { get; }
protected INugetPackageInfoProvider NugetPackageInfoProvider { get; }
protected ICliAnalyticsCollect CliAnalyticsCollect { get; }
protected AbpCliOptions Options { get; }
protected IJsonSerializer JsonSerializer { get; }
protected IApiKeyService ApiKeyService { get; }

public PackageProjectBuilder(ISourceCodeStore sourceCodeStore,
INugetPackageInfoProvider nugetPackageInfoProvider,
ICliAnalyticsCollect cliAnalyticsCollect,
IOptions<AbpCliOptions> options,
IJsonSerializer jsonSerializer,
IApiKeyService apiKeyService)
{
SourceCodeStore = sourceCodeStore;
NugetPackageInfoProvider = nugetPackageInfoProvider;
CliAnalyticsCollect = cliAnalyticsCollect;
Options = options.Value;
JsonSerializer = jsonSerializer;
ApiKeyService = apiKeyService;

Logger = NullLogger<PackageProjectBuilder>.Instance;
}

public async Task<ProjectBuildResult> BuildAsync(ProjectBuildArgs args)
{
var packageInfo = await GetPackageInfoAsync(args);

var templateFile = await SourceCodeStore.GetAsync(
args.TemplateName,
SourceCodeTypes.Package,
args.Version,
null,
args.ExtraProperties.ContainsKey(GetSourceCommand.Options.Preview.Long)
);

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,
null,
packageInfo,
templateFile,
args
);

PackageProjectBuildPipelineBuilder.Build(context).Execute();

// 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))
.Where(x => !x.Key.Equals(NewCommand.Options.TemplateSource.Short, StringComparison.InvariantCultureIgnoreCase) &&
!x.Key.Equals(NewCommand.Options.TemplateSource.Long, 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 = null,
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<NugetPackageInfo> GetPackageInfoAsync(ProjectBuildArgs args)
{
return await NugetPackageInfoProvider.GetAsync(args.TemplateName);
}
}
}