Browse Source

Merge pull request #2926 from abpframework/Linking-module-source-code-to-solution

Linking module source code to solution
pull/2953/head
Yunus Emre Kalkan 7 years ago
committed by GitHub
parent
commit
5c9d91dbb2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs
  2. 82
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs
  3. 82
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs
  4. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs
  5. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs
  6. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ReplaceCommonPropsStep.cs
  7. 75
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ReplaceConfigureAwaitPropsStep.cs
  8. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs
  9. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs
  10. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleWithMastersInfo.cs
  11. 127
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NugetPackageToLocalReferenceConverter.cs
  12. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs
  13. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs
  14. 36
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionAbpVersionFinder.cs
  15. 117
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionFileModifier.cs
  16. 68
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  17. 20
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/StreamHelper.cs

30
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs

@ -16,10 +16,12 @@ namespace Volo.Abp.Cli.Commands
public ILogger<AddModuleCommand> Logger { get; set; } public ILogger<AddModuleCommand> Logger { get; set; }
protected SolutionModuleAdder SolutionModuleAdder { get; } protected SolutionModuleAdder SolutionModuleAdder { get; }
public SolutionAbpVersionFinder SolutionAbpVersionFinder { get; }
public AddModuleCommand(SolutionModuleAdder solutionModuleAdder) public AddModuleCommand(SolutionModuleAdder solutionModuleAdder, SolutionAbpVersionFinder solutionAbpVersionFinder)
{ {
SolutionModuleAdder = solutionModuleAdder; SolutionModuleAdder = solutionModuleAdder;
SolutionAbpVersionFinder = solutionAbpVersionFinder;
Logger = NullLogger<AddModuleCommand>.Instance; Logger = NullLogger<AddModuleCommand>.Instance;
} }
@ -34,14 +36,26 @@ namespace Volo.Abp.Cli.Commands
); );
} }
var withSourceCode = commandLineArgs.Options.ContainsKey("with-source-code");
var skipDbMigrations = Convert.ToBoolean( var skipDbMigrations = Convert.ToBoolean(
commandLineArgs.Options.GetOrNull(Options.DbMigrations.Skip) ?? "false"); commandLineArgs.Options.GetOrNull(Options.DbMigrations.Skip) ?? "false");
var solutionFile = GetSolutionFile(commandLineArgs);
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
if (version == null)
{
version = SolutionAbpVersionFinder.Find(solutionFile);
}
await SolutionModuleAdder.AddAsync( await SolutionModuleAdder.AddAsync(
GetSolutionFile(commandLineArgs), solutionFile,
commandLineArgs.Target, commandLineArgs.Target,
commandLineArgs.Options.GetOrNull(Options.StartupProject.Short, Options.StartupProject.Long), commandLineArgs.Options.GetOrNull(Options.StartupProject.Short, Options.StartupProject.Long),
skipDbMigrations version,
skipDbMigrations,
withSourceCode
); );
} }
@ -57,8 +71,9 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine(" abp add-module <module-name> [options]"); sb.AppendLine(" abp add-module <module-name> [options]");
sb.AppendLine(""); sb.AppendLine("");
sb.AppendLine("Options:"); sb.AppendLine("Options:");
sb.AppendLine(" -s|--solution <solution-file> Specify the solution file explicitly."); sb.AppendLine(" --with-source-code Downloads the source code of the module and adds it to your solution.");
sb.AppendLine(" --skip-db-migrations <boolean> Specify if a new migration will be added or not."); sb.AppendLine(" -s|--solution <solution-file> Specify the solution file explicitly.");
sb.AppendLine(" --skip-db-migrations <boolean> Specify if a new migration will be added or not.");
sb.AppendLine(" -sp|--startup-project <startup-project-path> Relative path to the project folder of the startup project. Default value is the current folder."); sb.AppendLine(" -sp|--startup-project <startup-project-path> Relative path to the project folder of the startup project. Default value is the current folder.");
sb.AppendLine(""); sb.AppendLine("");
sb.AppendLine("Examples:"); sb.AppendLine("Examples:");
@ -126,6 +141,11 @@ namespace Volo.Abp.Cli.Commands
public const string Short = "s"; public const string Short = "s";
public const string Long = "solution"; public const string Long = "solution";
} }
public static class Version
{
public const string Short = "v";
public const string Long = "version";
}
public static class DbMigrations public static class DbMigrations
{ {

82
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs

@ -7,6 +7,7 @@ using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building; using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -15,12 +16,14 @@ namespace Volo.Abp.Cli.Commands
{ {
public class GetSourceCommand : IConsoleCommand, ITransientDependency public class GetSourceCommand : IConsoleCommand, ITransientDependency
{ {
private readonly SourceCodeDownloadService _sourceCodeDownloadService;
public ModuleProjectBuilder ModuleProjectBuilder { get; } public ModuleProjectBuilder ModuleProjectBuilder { get; }
public ILogger<NewCommand> Logger { get; set; } public ILogger<NewCommand> Logger { get; set; }
public GetSourceCommand(ModuleProjectBuilder moduleProjectBuilder) public GetSourceCommand(ModuleProjectBuilder moduleProjectBuilder, SourceCodeDownloadService sourceCodeDownloadService)
{ {
_sourceCodeDownloadService = sourceCodeDownloadService;
ModuleProjectBuilder = moduleProjectBuilder; ModuleProjectBuilder = moduleProjectBuilder;
Logger = NullLogger<NewCommand>.Instance; Logger = NullLogger<NewCommand>.Instance;
} }
@ -36,29 +39,13 @@ namespace Volo.Abp.Cli.Commands
); );
} }
Logger.LogInformation("Downloading source code of " + commandLineArgs.Target);
var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long); var version = commandLineArgs.Options.GetOrNull(Options.Version.Short, Options.Version.Long);
if (version != null) if (version != null)
{ {
Logger.LogInformation("Version: " + version); Logger.LogInformation("Version: " + version);
} }
var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long); var outputFolder = GetOutPutFolder(commandLineArgs);
if (outputFolder != null)
{
if (!Directory.Exists(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
outputFolder = Path.GetFullPath(outputFolder);
}
else
{
outputFolder = Directory.GetCurrentDirectory();
}
Logger.LogInformation("Output folder: " + outputFolder); Logger.LogInformation("Output folder: " + outputFolder);
var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long); var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long);
@ -69,53 +56,28 @@ namespace Volo.Abp.Cli.Commands
commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command); commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command);
var result = await ModuleProjectBuilder.BuildAsync( await _sourceCodeDownloadService.DownloadAsync(
new ProjectBuildArgs( commandLineArgs.Target, outputFolder, version, gitHubLocalRepositoryPath, commandLineArgs.Options);
SolutionName.Parse(commandLineArgs.Target), }
commandLineArgs.Target,
version, private static string GetOutPutFolder(CommandLineArgs commandLineArgs)
DatabaseProvider.NotSpecified, {
UiFramework.NotSpecified, var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long);
MobileApp.None, if (outputFolder != null)
gitHubLocalRepositoryPath,
commandLineArgs.Options
)
);
using (var templateFileStream = new MemoryStream(result.ZipContent))
{ {
using (var zipInputStream = new ZipInputStream(templateFileStream)) if (!Directory.Exists(outputFolder))
{ {
var zipEntry = zipInputStream.GetNextEntry(); Directory.CreateDirectory(outputFolder);
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();
}
} }
outputFolder = Path.GetFullPath(outputFolder);
}
else
{
outputFolder = Directory.GetCurrentDirectory();
} }
Logger.LogInformation($"'{commandLineArgs.Target}' has been successfully downloaded to '{outputFolder}'"); return outputFolder;
} }
public string GetUsageInfo() public string GetUsageInfo()

82
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
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.Services
{
public class SourceCodeDownloadService : ITransientDependency
{
public ModuleProjectBuilder ModuleProjectBuilder { get; }
public ILogger<SourceCodeDownloadService> Logger { get; set; }
public SourceCodeDownloadService(ModuleProjectBuilder moduleProjectBuilder)
{
ModuleProjectBuilder = moduleProjectBuilder;
Logger = NullLogger<SourceCodeDownloadService>.Instance;
}
public async Task DownloadAsync(string moduleName, string outputFolder, string version, string gitHubLocalRepositoryPath, AbpCommandLineOptions options)
{
Logger.LogInformation("Downloading source code of " + moduleName);
Logger.LogInformation("Version: " + version);
Logger.LogInformation("Output folder: " + outputFolder);
var result = await ModuleProjectBuilder.BuildAsync(
new ProjectBuildArgs(
SolutionName.Parse(moduleName),
moduleName,
version,
DatabaseProvider.NotSpecified,
UiFramework.NotSpecified,
gitHubLocalRepositoryPath,
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($"'{moduleName}' has been successfully downloaded to '{outputFolder}'");
}
}
}

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ModuleProjectBuildPipelineBuilder.cs

@ -11,6 +11,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building
pipeline.Steps.Add(new FileEntryListReadStep()); pipeline.Steps.Add(new FileEntryListReadStep());
pipeline.Steps.Add(new ProjectReferenceReplaceStep()); pipeline.Steps.Add(new ProjectReferenceReplaceStep());
pipeline.Steps.Add(new ReplaceCommonPropsStep()); pipeline.Steps.Add(new ReplaceCommonPropsStep());
pipeline.Steps.Add(new ReplaceConfigureAwaitPropsStep());
pipeline.Steps.Add(new CreateProjectResultZipStep()); pipeline.Steps.Add(new CreateProjectResultZipStep());
return pipeline; return pipeline;

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs

@ -4,6 +4,7 @@ using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using Volo.Abp.Cli.ProjectBuilding.Files; using Volo.Abp.Cli.ProjectBuilding.Files;
using Volo.Abp.Cli.Utils;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{ {
@ -92,7 +93,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
var doc = new XmlDocument() { PreserveWhitespace = true }; var doc = new XmlDocument() { PreserveWhitespace = true };
doc.Load(GenerateStreamFromString(content)); doc.Load(StreamHelper.GenerateStreamFromString(content));
return ProcessReferenceNodes(doc, content); return ProcessReferenceNodes(doc, content);
} }
@ -125,16 +126,6 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
protected abstract XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue); protected abstract XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue);
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;
}
public class NugetReferenceReplacer : ProjectReferenceReplacer public class NugetReferenceReplacer : ProjectReferenceReplacer
{ {

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ReplaceCommonPropsStep.cs

@ -4,6 +4,7 @@ using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using Volo.Abp.Cli.ProjectBuilding.Files; using Volo.Abp.Cli.ProjectBuilding.Files;
using Volo.Abp.Cli.Utils;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{ {
@ -41,7 +42,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
var doc = new XmlDocument() { PreserveWhitespace = true }; var doc = new XmlDocument() { PreserveWhitespace = true };
doc.Load(GenerateStreamFromString(content)); doc.Load(StreamHelper.GenerateStreamFromString(content));
return ProcessReferenceNodes(doc, content); return ProcessReferenceNodes(doc, content);
} }
@ -81,16 +82,6 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
return doc.OuterXml; return doc.OuterXml;
} }
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;
}
} }
} }
} }

75
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ReplaceConfigureAwaitPropsStep.cs

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;
using Volo.Abp.Cli.ProjectBuilding.Files;
using Volo.Abp.Cli.Utils;
namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
{
public class ReplaceConfigureAwaitPropsStep : ProjectBuildPipelineStep
{
public override void Execute(ProjectBuildContext context)
{
new ConfigureAwaitPropsReplacer(context.Files).Run();
}
private class ConfigureAwaitPropsReplacer
{
private readonly List<FileEntry> _entries;
public ConfigureAwaitPropsReplacer(
List<FileEntry> entries)
{
_entries = entries;
}
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 XmlDocument() { PreserveWhitespace = true };
doc.Load(StreamHelper.GenerateStreamFromString(content));
return ProcessReferenceNodes(doc, content);
}
private string ProcessReferenceNodes(XmlDocument doc, string content)
{
Check.NotNull(content, nameof(content));
var importNodes = doc.SelectNodes("/Project/Import[@Project]");
if (importNodes == null)
{
return doc.OuterXml;
}
foreach (XmlNode node in importNodes)
{
if (!(node.Attributes?["Project"]?.Value?.EndsWith("\\configureawait.props") ?? false))
{
continue;
}
node.ParentNode?.RemoveChild(node);
}
return doc.OuterXml;
}
}
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs

@ -18,7 +18,7 @@ namespace Volo.Abp.Cli.ProjectModification
Logger = NullLogger<DbContextFileBuilderConfigureAdder>.Instance; Logger = NullLogger<DbContextFileBuilderConfigureAdder>.Instance;
} }
public void Add(string path, string moduleConfiguration) public bool Add(string path, string moduleConfiguration)
{ {
var file = File.ReadAllText(path); var file = File.ReadAllText(path);
@ -32,13 +32,18 @@ namespace Volo.Abp.Cli.ProjectModification
if (indexToInsert <= 0 || indexToInsert >= file.Length) if (indexToInsert <= 0 || indexToInsert >= file.Length)
{ {
Logger.LogWarning($"\"OnModelCreating(ModelBuilder builder)\" method couldn't be found in {path}"); Logger.LogWarning($"\"OnModelCreating(ModelBuilder builder)\" method couldn't be found in {path}");
return; return false;
} }
file = file.Insert(indexToInsert, " " + stringToAdd + Environment.NewLine + " "); file = file.Insert(indexToInsert, " " + stringToAdd + Environment.NewLine + " ");
} }
else
{
return false;
}
File.WriteAllText(path, file); File.WriteAllText(path, file);
return true;
} }
protected int FindIndexToInsert(string file) protected int FindIndexToInsert(string file)

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs

@ -53,7 +53,6 @@ namespace Volo.Abp.Cli.ProjectModification
protected bool IsDerived(string csFile, string baseClass) protected bool IsDerived(string csFile, string baseClass)
{ {
Logger.LogDebug(csFile);
var root = CSharpSyntaxTree.ParseText(File.ReadAllText(csFile)).GetRoot(); var root = CSharpSyntaxTree.ParseText(File.ReadAllText(csFile)).GetRoot();
var namespaceSyntax = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().First(); var namespaceSyntax = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().First();
var classDeclaration = (namespaceSyntax.DescendantNodes().OfType<ClassDeclarationSyntax>()).First(); var classDeclaration = (namespaceSyntax.DescendantNodes().OfType<ClassDeclarationSyntax>()).First();

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleWithMastersInfo.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.Cli.ProjectModification
{
public class ModuleWithMastersInfo : ModuleInfo
{
public List<ModuleWithMastersInfo> MasterModuleInfos { get; set; }
}
}

127
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NugetPackageToLocalReferenceConverter.cs

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectModification
{
public class NugetPackageToLocalReferenceConverter : ITransientDependency
{
public async Task Convert(ModuleWithMastersInfo module, string solutionFile)
{
var nugetPackageList = GetNugetPackages(module);
var projectFilesUnderModules = GetProjectFilesUnder(Path.Combine(Path.GetDirectoryName(solutionFile), "modules"));
var projectFilesUnderSrc = GetProjectFilesUnder(Path.Combine(Path.GetDirectoryName(solutionFile), "src"));
var projectFilesUnderTest = GetProjectFilesUnder(Path.Combine(Path.GetDirectoryName(solutionFile), "test"));
foreach (var projectFile in projectFilesUnderModules)
{
ConvertToLocalReference(projectFile, nugetPackageList, "..\\..\\..\\");
}
foreach (var projectFile in projectFilesUnderSrc)
{
ConvertToLocalReference(projectFile, nugetPackageList, "..\\..\\modules\\");
}
foreach (var projectFile in projectFilesUnderTest)
{
ConvertToLocalReference(projectFile, nugetPackageList, "..\\..\\modules\\", "test");
}
}
private void ConvertToLocalReference(string projectFile, List<NugetPackageInfoWithModuleName> nugetPackageList, string localPathPrefix, string sourceFile = "src")
{
var content = File.ReadAllText(projectFile);
var doc = new XmlDocument() { PreserveWhitespace = true };
doc.Load(StreamHelper.GenerateStreamFromString(content));
var convertedProject = ProcessReferenceNodes(doc, nugetPackageList, localPathPrefix, sourceFile);
File.WriteAllText(projectFile, convertedProject);
}
private string ProcessReferenceNodes(XmlDocument doc, List<NugetPackageInfoWithModuleName> nugetPackageList, string localPathPrefix, string sourceFile = "src")
{
var nodes = doc.SelectNodes("/Project/ItemGroup/PackageReference[@Include]");
if (nodes == null)
{
return doc.OuterXml;
}
foreach (XmlNode oldNode in nodes)
{
var oldNodeIncludeValue = oldNode?.Attributes?["Include"]?.Value;
var nugetPackage = nugetPackageList.FirstOrDefault(n => n.NugetPackage.Name == oldNodeIncludeValue);
if (nugetPackage == null)
{
continue;
}
var referenceProjectPath =
$"{localPathPrefix}{nugetPackage.ModuleName}\\{sourceFile}\\{nugetPackage.NugetPackage.Name}\\{nugetPackage.NugetPackage.Name}.csproj";
XmlNode newNode = GetNewReferenceNode(doc, referenceProjectPath);
oldNode?.ParentNode?.ReplaceChild(newNode, oldNode);
}
return doc.OuterXml;
}
protected XmlElement GetNewReferenceNode(XmlDocument doc, string newValue)
{
var newNode = doc.CreateElement("ProjectReference");
var includeAttr = doc.CreateAttribute("Include");
includeAttr.Value = newValue;
newNode.Attributes.Append(includeAttr);
return newNode;
}
public List<NugetPackageInfoWithModuleName> GetNugetPackages(ModuleWithMastersInfo module)
{
var list = new List<NugetPackageInfoWithModuleName>();
list.AddRange(module.NugetPackages.Select(n => new NugetPackageInfoWithModuleName
{
ModuleName = module.Name,
NugetPackage = n
}));
if (module.MasterModuleInfos != null)
{
foreach (var masterModule in module.MasterModuleInfos)
{
list.AddRange(GetNugetPackages(masterModule));
}
}
return list;
}
private static string[] GetProjectFilesUnder(string path)
{
return Directory.GetFiles(path,
"*.csproj",
SearchOption.AllDirectories);
}
public class NugetPackageInfoWithModuleName
{
public NugetPackageInfo NugetPackage { get; set; }
public string ModuleName { get; set; }
}
}
}

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs

@ -20,7 +20,7 @@ namespace Volo.Abp.Cli.ProjectModification
public Task AddAsync(string directory, NpmPackageInfo npmPackage) public Task AddAsync(string directory, NpmPackageInfo npmPackage)
{ {
var packageJsonFilePath = Path.Combine(directory, "package.json"); var packageJsonFilePath = Path.Combine(directory, "package.json");
if (!File.Exists(packageJsonFilePath)) if (!File.Exists(packageJsonFilePath) || File.ReadAllText(packageJsonFilePath).Contains($"\"{npmPackage.Name}\""))
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs

@ -50,6 +50,11 @@ namespace Volo.Abp.Cli.ProjectModification
public Task AddAsync(string projectFile, NugetPackageInfo package) public Task AddAsync(string projectFile, NugetPackageInfo package)
{ {
if (File.ReadAllText(projectFile).Contains($"\"{package.Name}\""))
{
return Task.CompletedTask;
}
using (DirectoryHelper.ChangeCurrentDirectory(Path.GetDirectoryName(projectFile))) using (DirectoryHelper.ChangeCurrentDirectory(Path.GetDirectoryName(projectFile)))
{ {
Logger.LogInformation($"Installing '{package.Name}' package to the project '{Path.GetFileNameWithoutExtension(projectFile)}'..."); Logger.LogInformation($"Installing '{package.Name}' package to the project '{Path.GetFileNameWithoutExtension(projectFile)}'...");

36
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionAbpVersionFinder.cs

@ -0,0 +1,36 @@
using System.IO;
using System.Xml;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectModification
{
public class SolutionAbpVersionFinder : ITransientDependency
{
public string Find(string solutionFile)
{
var projectFilesUnderSrc = Directory.GetFiles(Path.GetDirectoryName(solutionFile),
"*.csproj",
SearchOption.AllDirectories);
foreach (var projectFile in projectFilesUnderSrc)
{
var content = File.ReadAllText(projectFile);
var doc = new XmlDocument() { PreserveWhitespace = true };
doc.Load(StreamHelper.GenerateStreamFromString(content));
var nodes = doc.SelectNodes("/Project/ItemGroup/PackageReference[starts-with(@Include, 'Volo.Abp')]");
var value = nodes?[0]?.Attributes?["Version"]?.Value;
if (value != null)
{
return value;
}
}
return null;
}
}
}

117
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionFileModifier.cs

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectModification
{
public class SolutionFileModifier : ITransientDependency
{
public async Task AddModuleToSolutionFileAsync(ModuleWithMastersInfo module, string solutionFile)
{
await AddModule(module, solutionFile);
}
private async Task AddModule(ModuleWithMastersInfo module, string solutionFile)
{
var srcModuleFolderId = await AddNewFolderAndGetIdOrGetExistingId(solutionFile, module.Name, await AddNewFolderAndGetIdOrGetExistingId(solutionFile, "modules"));
var testModuleFolderId = await AddNewFolderAndGetIdOrGetExistingId(solutionFile, module.Name + ".Tests", await AddNewFolderAndGetIdOrGetExistingId(solutionFile, "test"));
var file = File.ReadAllText(solutionFile);
var lines = file.Split(Environment.NewLine).ToList();
var projectsUnderModule = Directory.GetFiles(
Path.Combine(Path.GetDirectoryName(solutionFile), "modules", module.Name),
"*.csproj",
SearchOption.AllDirectories);
var projectsUnderTest = Directory.GetFiles(
Path.Combine(Path.GetDirectoryName(solutionFile), "modules", module.Name, "test"),
"*.csproj",
SearchOption.AllDirectories);
foreach (var projectPath in projectsUnderModule)
{
var parentFolderId = projectsUnderTest.Contains(projectPath) ? testModuleFolderId : srcModuleFolderId;
var projectId = Path.GetFileName(projectPath).Replace(".csproj","");
var projectParentFolderInModule = projectsUnderTest.Contains(projectPath) ? "test" : "src";
if (lines.Any(l => l.Contains($"\"{projectId}\"")))
{
continue;
}
var projectGuid = Guid.NewGuid().ToString();
var newProjectLine = "Project(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"" + projectId + "\"," +
" \"modules\\" + module.Name + "\\"+ projectParentFolderInModule + "\\" + projectId + "\\" + projectId + ".csproj\", \"{" + projectGuid + "}\""
+ Environment.NewLine + "EndProject";
lines.InsertAfter(l => l.Trim().Equals("EndProject"), newProjectLine);
var newPostSolutionLine =
" {" + projectGuid + "}.Debug|Any CPU.ActiveCfg = Debug|Any CPU" + Environment.NewLine +
" {" + projectGuid + "}.Debug|Any CPU.Build.0 = Debug|Any CPU" + Environment.NewLine +
" {" + projectGuid + "}.Release|Any CPU.ActiveCfg = Release|Any CPU" + Environment.NewLine +
" {" + projectGuid + "}.Release|Any CPU.Build.0 = Release|Any CPU";
lines.InsertAfter(l=>l.Contains("GlobalSection") && l.Contains("ProjectConfigurationPlatforms"), newPostSolutionLine);
var newPreSolutionLine =
" {"+ projectGuid + "} = {"+ parentFolderId + "}";
lines.InsertAfter(l=>l.Contains("GlobalSection") && l.Contains("NestedProjects"), newPreSolutionLine);
}
File.WriteAllText(solutionFile, string.Join(Environment.NewLine, lines));
if (module.MasterModuleInfos != null)
{
foreach (var masterModule in module.MasterModuleInfos)
{
await AddModule(masterModule, solutionFile);
}
}
}
private async Task<string> AddNewFolderAndGetIdOrGetExistingId(string solutionFile, string folderName, string parentFolderId = null)
{
var file = File.ReadAllText(solutionFile);
var lines = file.Split(Environment.NewLine).ToList();
string folderId;
var folderLineIndex = lines.FindIndex(l =>
l.Contains("2150E333-8FDC-42A3-9474-1A3956D46DE8") && l.Contains("\""+ folderName + "\""));
if (folderLineIndex < 0)
{
folderId = Guid.NewGuid().ToString();
var newFolderLine = "Project(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \""+ folderName + "\", \""+ folderName + "\", \"{" + folderId + "}\""
+ Environment.NewLine + "EndProject";
lines.InsertAfter(l => l.Trim().Equals("EndProject"), newFolderLine);
if (parentFolderId != null)
{
var newPreSolutionLine =
" {" + folderId + "} = {" + parentFolderId + "}";
lines.InsertAfter(l => l.Contains("GlobalSection") && l.Contains("NestedProjects"), newPreSolutionLine);
}
}
else
{
folderId = lines[folderLineIndex].Replace("\"", " ").Replace("{", " ").Replace("}", " ").TrimEnd()
.Split(" ").Last();
}
File.WriteAllText(solutionFile, string.Join(Environment.NewLine, lines));
return folderId;
}
}
}

68
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -7,6 +7,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -26,6 +27,9 @@ namespace Volo.Abp.Cli.ProjectModification
protected ProjectNpmPackageAdder ProjectNpmPackageAdder { get; } protected ProjectNpmPackageAdder ProjectNpmPackageAdder { get; }
protected NpmGlobalPackagesChecker NpmGlobalPackagesChecker { get; } protected NpmGlobalPackagesChecker NpmGlobalPackagesChecker { get; }
protected IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; } protected IRemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; }
public SourceCodeDownloadService SourceCodeDownloadService { get; }
public SolutionFileModifier SolutionFileModifier { get; }
public NugetPackageToLocalReferenceConverter NugetPackageToLocalReferenceConverter { get; }
public SolutionModuleAdder( public SolutionModuleAdder(
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
@ -35,7 +39,10 @@ namespace Volo.Abp.Cli.ProjectModification
DerivedClassFinder derivedClassFinder, DerivedClassFinder derivedClassFinder,
ProjectNpmPackageAdder projectNpmPackageAdder, ProjectNpmPackageAdder projectNpmPackageAdder,
NpmGlobalPackagesChecker npmGlobalPackagesChecker, NpmGlobalPackagesChecker npmGlobalPackagesChecker,
IRemoteServiceExceptionHandler remoteServiceExceptionHandler) IRemoteServiceExceptionHandler remoteServiceExceptionHandler,
SourceCodeDownloadService sourceCodeDownloadService,
SolutionFileModifier solutionFileModifier,
NugetPackageToLocalReferenceConverter nugetPackageToLocalReferenceConverter)
{ {
JsonSerializer = jsonSerializer; JsonSerializer = jsonSerializer;
ProjectNugetPackageAdder = projectNugetPackageAdder; ProjectNugetPackageAdder = projectNugetPackageAdder;
@ -45,6 +52,9 @@ namespace Volo.Abp.Cli.ProjectModification
ProjectNpmPackageAdder = projectNpmPackageAdder; ProjectNpmPackageAdder = projectNpmPackageAdder;
NpmGlobalPackagesChecker = npmGlobalPackagesChecker; NpmGlobalPackagesChecker = npmGlobalPackagesChecker;
RemoteServiceExceptionHandler = remoteServiceExceptionHandler; RemoteServiceExceptionHandler = remoteServiceExceptionHandler;
SourceCodeDownloadService = sourceCodeDownloadService;
SolutionFileModifier = solutionFileModifier;
NugetPackageToLocalReferenceConverter = nugetPackageToLocalReferenceConverter;
Logger = NullLogger<SolutionModuleAdder>.Instance; Logger = NullLogger<SolutionModuleAdder>.Instance;
} }
@ -52,7 +62,9 @@ namespace Volo.Abp.Cli.ProjectModification
[NotNull] string solutionFile, [NotNull] string solutionFile,
[NotNull] string moduleName, [NotNull] string moduleName,
string startupProject, string startupProject,
bool skipDbMigrations = false) string version,
bool skipDbMigrations = false,
bool withSourceCode = false)
{ {
Check.NotNull(solutionFile, nameof(solutionFile)); Check.NotNull(solutionFile, nameof(solutionFile));
Check.NotNull(moduleName, nameof(moduleName)); Check.NotNull(moduleName, nameof(moduleName));
@ -63,6 +75,42 @@ namespace Volo.Abp.Cli.ProjectModification
var projectFiles = ProjectFinder.GetProjectFiles(solutionFile); var projectFiles = ProjectFinder.GetProjectFiles(solutionFile);
await AddNugetAndNpmReferences(module, projectFiles);
if (withSourceCode)
{
var modulesFolderInSolution = Path.Combine(Path.GetDirectoryName(solutionFile), "modules");
await DownloadSourceCodesToSolutionFolder(module, modulesFolderInSolution, version);
await SolutionFileModifier.AddModuleToSolutionFileAsync(module, solutionFile);
await NugetPackageToLocalReferenceConverter.Convert(module, solutionFile);
}
ModifyDbContext(projectFiles, module, startupProject, skipDbMigrations);
}
private async Task DownloadSourceCodesToSolutionFolder(ModuleWithMastersInfo module, string modulesFolderInSolution, string version = null)
{
await SourceCodeDownloadService.DownloadAsync(
module.Name,
Path.Combine(modulesFolderInSolution, module.Name),
version,
null,
null
);
if (module.MasterModuleInfos == null)
{
return;
}
foreach (var masterModule in module.MasterModuleInfos)
{
await DownloadSourceCodesToSolutionFolder(masterModule, modulesFolderInSolution, version);
}
}
private async Task AddNugetAndNpmReferences(ModuleWithMastersInfo module, string[] projectFiles)
{
foreach (var nugetPackage in module.NugetPackages) foreach (var nugetPackage in module.NugetPackages)
{ {
var targetProjectFile = ProjectFinder.FindNuGetTargetProjectFile(projectFiles, nugetPackage.Target); var targetProjectFile = ProjectFinder.FindNuGetTargetProjectFile(projectFiles, nugetPackage.Target);
@ -84,7 +132,8 @@ namespace Volo.Abp.Cli.ProjectModification
foreach (var targetProject in targetProjects) foreach (var targetProject in targetProjects)
{ {
foreach (var npmPackage in module.NpmPackages.Where(p => p.ApplicationType.HasFlag(NpmApplicationType.Mvc))) foreach (var npmPackage in module.NpmPackages.Where(p =>
p.ApplicationType.HasFlag(NpmApplicationType.Mvc)))
{ {
await ProjectNpmPackageAdder.AddAsync(Path.GetDirectoryName(targetProject), npmPackage); await ProjectNpmPackageAdder.AddAsync(Path.GetDirectoryName(targetProject), npmPackage);
} }
@ -95,8 +144,6 @@ namespace Volo.Abp.Cli.ProjectModification
Logger.LogDebug("Target project is not available for NPM packages."); Logger.LogDebug("Target project is not available for NPM packages.");
} }
} }
ModifyDbContext(projectFiles, module, startupProject, skipDbMigrations);
} }
protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, string startupProject, bool skipDbMigrations = false) protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, string startupProject, bool skipDbMigrations = false)
@ -127,20 +174,19 @@ namespace Volo.Abp.Cli.ProjectModification
return; return;
} }
DbContextFileBuilderConfigureAdder.Add(dbContextFile, module.EfCoreConfigureMethodName); var addedNewBuilder = DbContextFileBuilderConfigureAdder.Add(dbContextFile, module.EfCoreConfigureMethodName);
if (!skipDbMigrations) if (addedNewBuilder && !skipDbMigrations)
{ {
EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject); EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject);
} }
} }
protected virtual async Task<ModuleInfo> FindModuleInfoAsync(string moduleName) protected virtual async Task<ModuleWithMastersInfo> FindModuleInfoAsync(string moduleName)
{ {
using (var client = new CliHttpClient()) using (var client = new CliHttpClient())
{ {
var url = $"{CliUrls.WwwAbpIo}api/app/module/byName/?name=" + moduleName; var url = $"{CliUrls.WwwAbpIo}api/app/module/byNameWithDetails/?name=" + moduleName;
var response = await client.GetAsync(url); var response = await client.GetAsync(url);
@ -155,7 +201,7 @@ namespace Volo.Abp.Cli.ProjectModification
} }
var responseContent = await response.Content.ReadAsStringAsync(); var responseContent = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<ModuleInfo>(responseContent); return JsonSerializer.Deserialize<ModuleWithMastersInfo>(responseContent);
} }
} }
} }

20
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/StreamHelper.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Volo.Abp.Cli.Utils
{
public static class StreamHelper
{
public static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
}
Loading…
Cancel
Save