mirror of https://github.com/abpframework/abp.git
16 changed files with 908 additions and 0 deletions
@ -0,0 +1,188 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Xml.Linq; |
|||
using LibGit2Sharp; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public class DefaultChangedProjectFinder : IChangedProjectFinder, ITransientDependency |
|||
{ |
|||
private readonly IRepositoryBuildStatusStore _repositoryBuildStatusStore; |
|||
|
|||
public DefaultChangedProjectFinder(IRepositoryBuildStatusStore repositoryBuildStatusStore) |
|||
{ |
|||
_repositoryBuildStatusStore = repositoryBuildStatusStore; |
|||
} |
|||
|
|||
public List<DotNetProjectInfo> Find(DotNetProjectBuildConfig buildConfig) |
|||
{ |
|||
if (buildConfig.SlFilePath.IsNullOrEmpty()) |
|||
{ |
|||
return FindByRepository(buildConfig.GitRepository); |
|||
} |
|||
|
|||
return FindBySlnFile(buildConfig.GitRepository, buildConfig.SlFilePath); |
|||
} |
|||
|
|||
private List<DotNetProjectInfo> FindByRepository(GitRepository gitRepository) |
|||
{ |
|||
var changedProjectList = new List<DotNetProjectInfo>(); |
|||
var gitRepositoryBuildStatus = _repositoryBuildStatusStore.Get(gitRepository); |
|||
|
|||
FindChangedFiles(gitRepository, gitRepositoryBuildStatus, changedProjectList); |
|||
|
|||
return changedProjectList; |
|||
} |
|||
|
|||
private List<DotNetProjectInfo> FindBySlnFile(GitRepository gitRepository, string slnFilePath) |
|||
{ |
|||
var slFine = new FileInfo(slnFilePath); |
|||
var changedProjectList = new List<DotNetProjectInfo>(); |
|||
var csProjFiles = slFine.Directory.GetFiles( |
|||
"*.csproj", |
|||
SearchOption.AllDirectories |
|||
).Select(e => e.FullName) |
|||
.ToList(); |
|||
|
|||
foreach (var csProjFile in csProjFiles) |
|||
{ |
|||
AddDependingProjectsToList(gitRepository, csProjFile, changedProjectList); |
|||
} |
|||
|
|||
return changedProjectList; |
|||
} |
|||
|
|||
private void AddDependingProjectsToList( |
|||
GitRepository gitRepository, |
|||
string csProjFilePath, |
|||
List<DotNetProjectInfo> changedProjectList) |
|||
{ |
|||
var repositoryName = gitRepository.FindRepositoryOf(csProjFilePath); |
|||
var project = new DotNetProjectInfo(repositoryName, csProjFilePath); |
|||
if (changedProjectList.Any(e => e.CsProjPath == csProjFilePath)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
changedProjectList.Add(project); |
|||
|
|||
AddProjectDependencies(gitRepository, project, changedProjectList); |
|||
} |
|||
|
|||
private void AddProjectDependencies(GitRepository gitRepository, DotNetProjectInfo project, List<DotNetProjectInfo> changedProjectList) |
|||
{ |
|||
var projectNode = XElement.Load(project.CsProjPath); |
|||
var referenceNodes = projectNode.Descendants("ItemGroup").Descendants("ProjectReference"); |
|||
|
|||
foreach (var referenceNode in referenceNodes) |
|||
{ |
|||
if (referenceNode.Attribute("Include") == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var relativePath = referenceNode.Attribute("Include").Value; |
|||
var file = new FileInfo(project.CsProjPath); |
|||
var referenceProjectInfo = new FileInfo(Path.Combine(file.Directory.FullName, relativePath)); |
|||
|
|||
if (changedProjectList.Any(e => e.CsProjPath == referenceProjectInfo.FullName)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var repositoryName = gitRepository.FindRepositoryOf(referenceProjectInfo.FullName); |
|||
var referenceProject = new DotNetProjectInfo(repositoryName, referenceProjectInfo.FullName); |
|||
changedProjectList.Add(referenceProject); |
|||
|
|||
AddProjectDependencies(gitRepository, referenceProject, changedProjectList); |
|||
} |
|||
} |
|||
|
|||
private void FindChangedFiles( |
|||
GitRepository repository, |
|||
GitRepositoryBuildStatus repositoryBuildStatus, |
|||
List<DotNetProjectInfo> changedProjectList) |
|||
{ |
|||
if (repositoryBuildStatus.CommitId.IsNullOrEmpty()) |
|||
{ |
|||
AddAllCsProjFiles(repository, changedProjectList); |
|||
} |
|||
else |
|||
{ |
|||
AddChangedCsProjFiles(repository, changedProjectList, repositoryBuildStatus); |
|||
} |
|||
|
|||
if (repository.DependingRepositories.Any()) |
|||
{ |
|||
foreach (var dependingRepository in repository.DependingRepositories) |
|||
{ |
|||
var dependingRepositoryBuildStatus = repositoryBuildStatus.GetChild(dependingRepository.Name); |
|||
FindChangedFiles(dependingRepository, dependingRepositoryBuildStatus, changedProjectList); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void AddAllCsProjFiles(GitRepository repository, List<DotNetProjectInfo> changedFiles) |
|||
{ |
|||
var allCsProjFiles = Directory.GetFiles( |
|||
repository.RootPath, |
|||
"*.csproj", |
|||
SearchOption.AllDirectories |
|||
); |
|||
|
|||
foreach (var file in allCsProjFiles) |
|||
{ |
|||
changedFiles.Add( |
|||
new DotNetProjectInfo(repository.Name, Path.Combine(repository.RootPath, file)) |
|||
); |
|||
} |
|||
} |
|||
|
|||
private void AddChangedCsProjFiles( |
|||
GitRepository repository, |
|||
List<DotNetProjectInfo> changedFiles, |
|||
GitRepositoryBuildStatus status) |
|||
{ |
|||
using (var repo = new Repository(string.Concat(repository.RootPath, @"\.git"))) |
|||
{ |
|||
var firstCommit = status.CommitId.IsNullOrEmpty() |
|||
? null |
|||
: repo.Lookup<Commit>(status.CommitId); |
|||
var repoDifferences = repo.Diff.Compare<Patch>(firstCommit?.Tree, repo.Head.Tip.Tree); |
|||
|
|||
var files = repoDifferences |
|||
.Where(e => e.Path.EndsWith(".cs") || e.Path.EndsWith(".csproj")) |
|||
.Where(e => e.Status != ChangeKind.Deleted) |
|||
.Select(e => e) |
|||
.ToList(); |
|||
|
|||
foreach (var file in files) |
|||
{ |
|||
var csProjPath = Path.Combine(repository.RootPath, file.Path); |
|||
if (status.SucceedProjects.Any(p => p.CsProjPath == csProjPath && p.CommitId == "1")) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
changedFiles.Add( |
|||
new DotNetProjectInfo(repository.Name, csProjPath) |
|||
); |
|||
} |
|||
|
|||
if (!repository.DependingRepositories.Any()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var subRepository in repository.DependingRepositories) |
|||
{ |
|||
var subRepositoryBuildStatus = status.GetChild(subRepository.Name); |
|||
FindChangedFiles(subRepository, subRepositoryBuildStatus, changedFiles); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Volo.Abp.Cli.Utils; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public class DefaultDotNetProjectBuilder : IDotNetProjectBuilder, ITransientDependency |
|||
{ |
|||
public List<string> Build(List<DotNetProjectInfo> projects, int maxParallelBuildCount, string arguments) |
|||
{ |
|||
var builtProjects = new ConcurrentBag<string>(); |
|||
|
|||
try |
|||
{ |
|||
foreach (var project in projects) |
|||
{ |
|||
if (builtProjects.Contains(project.CsProjPath)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
BuildInternal(project, arguments, builtProjects); |
|||
|
|||
if (!project.Dependencies.Any()) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
// TODO: use parallel foreach here.
|
|||
foreach (var projectDependency in project.Dependencies) |
|||
{ |
|||
if (builtProjects.Contains(project.CsProjPath)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
BuildInternal(projectDependency, arguments, builtProjects); |
|||
} |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Console.WriteLine(e); |
|||
} |
|||
|
|||
return builtProjects.ToList(); |
|||
} |
|||
|
|||
private void BuildInternal(DotNetProjectInfo project, string arguments, ConcurrentBag<string> builtProjects) |
|||
{ |
|||
Console.WriteLine("Building...: dotnet build " + project.CsProjPath + " " + arguments); |
|||
|
|||
CmdHelper.RunCmdAndGetOutput( |
|||
"dotnet build " + project.CsProjPath + " " + arguments, |
|||
out int buildStatus |
|||
); |
|||
|
|||
if (buildStatus == 0) |
|||
{ |
|||
builtProjects.Add(project.CsProjPath); |
|||
} |
|||
else |
|||
{ |
|||
// TODO: throw exception and stop build process.
|
|||
Console.WriteLine("Build failed for :" + project.CsProjPath); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public class DotNetProjectBuildConfig |
|||
{ |
|||
public string SlFilePath { get; set; } |
|||
|
|||
public GitRepository GitRepository { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public class DotNetProjectBuildStatus |
|||
{ |
|||
public string CsProjPath { get; set; } |
|||
|
|||
public string CommitId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Xml.Linq; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public class DotNetProjectDependencyFiller : IDotNetProjectDependencyFiller, ITransientDependency |
|||
{ |
|||
public ILogger<DotNetProjectDependencyFiller> Logger { get; set; } |
|||
|
|||
public void Fill(List<DotNetProjectInfo> projects) |
|||
{ |
|||
foreach (var project in projects) |
|||
{ |
|||
FillProjectDependencies(project); |
|||
} |
|||
} |
|||
|
|||
private void FillProjectDependencies(DotNetProjectInfo project) |
|||
{ |
|||
var projectNode = XElement.Load(project.CsProjPath); |
|||
var referenceNodes = projectNode.Descendants("ItemGroup").Descendants("ProjectReference"); |
|||
|
|||
foreach (var referenceNode in referenceNodes) |
|||
{ |
|||
if (referenceNode.Attribute("Include") == null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var relativePath = referenceNode.Attribute("Include").Value; |
|||
var file = new FileInfo(project.CsProjPath); |
|||
var referenceProjectInfo = new FileInfo(Path.Combine(file.Directory.FullName, relativePath)); |
|||
|
|||
var referenceProject = new DotNetProjectInfo(project.RepositoryName, referenceProjectInfo.FullName); |
|||
|
|||
project.Dependencies.Add(referenceProject); |
|||
} |
|||
} |
|||
|
|||
private void GetProjectsOfRepository(GitRepository repository, List<string> projectPaths) |
|||
{ |
|||
Logger.LogInformation("repo name:" + repository.Name); |
|||
Logger.LogInformation("directoryPath:" + repository.RootPath); |
|||
|
|||
GetProjectsOfADirectory(repository.RootPath, projectPaths, new List<string>()); |
|||
|
|||
if (!repository.DependingRepositories.Any()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var dependantRepository in repository.DependingRepositories) |
|||
{ |
|||
GetProjectsOfRepository(dependantRepository, projectPaths); |
|||
} |
|||
} |
|||
|
|||
private void GetProjectsOfADirectory( |
|||
string directoryPath, |
|||
List<string> projectPaths, |
|||
List<string> ignoredDirectories) |
|||
{ |
|||
Logger.LogInformation("directoryPath:" + directoryPath); |
|||
|
|||
var files = Directory.GetFiles(directoryPath, "*.csproj", SearchOption.AllDirectories) |
|||
.Select(f => f) |
|||
.ToList(); |
|||
|
|||
// foreach (var ignoredDirectory in ignoredDirectories)
|
|||
// {
|
|||
// files = files.Where(e => !e.StartsWith(Path.Combine(directoryPath, ignoredDirectory)))
|
|||
// .ToList();
|
|||
// }
|
|||
|
|||
projectPaths.AddRange(files); |
|||
|
|||
// foreach (var projectFile in files)
|
|||
// {
|
|||
// var projectNode = XElement.Load(projectFile);
|
|||
// if (projectNode.Attribute("Sdk") == null)
|
|||
// {
|
|||
// continue;
|
|||
// }
|
|||
//
|
|||
// projectPaths.Add(projectFile);
|
|||
// }
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
[Serializable] |
|||
public class DotNetProjectInfo |
|||
{ |
|||
public string RepositoryName { get; set; } |
|||
|
|||
public string CsProjPath { get; set; } |
|||
|
|||
public List<DotNetProjectInfo> Dependencies { get; set; } |
|||
|
|||
public DotNetProjectInfo(string repositoryName, string csProjPath) |
|||
{ |
|||
RepositoryName = repositoryName; |
|||
CsProjPath = csProjPath; |
|||
Dependencies = new List<DotNetProjectInfo>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a source code repository
|
|||
/// </summary>
|
|||
public class GitRepository |
|||
{ |
|||
/// <summary>
|
|||
/// Name of the repository
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Branch of the repository
|
|||
/// </summary>
|
|||
public string BranchName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Root path of the repository which contains .git folder
|
|||
/// </summary>
|
|||
public string RootPath { get; set; } |
|||
|
|||
public List<GitRepository> DependingRepositories { get; set; } |
|||
|
|||
public GitRepository(string name, string branchName, string rootPath) |
|||
{ |
|||
Name = name; |
|||
BranchName = branchName; |
|||
RootPath = rootPath; |
|||
DependingRepositories = new List<GitRepository>(); |
|||
} |
|||
|
|||
public string GetUniqueName() |
|||
{ |
|||
var name = Name + "_" + BranchName; |
|||
foreach (var dependingRepository in DependingRepositories) |
|||
{ |
|||
AddToUniqueName(dependingRepository, name); |
|||
} |
|||
|
|||
return name.ToMd5(); |
|||
} |
|||
|
|||
private void AddToUniqueName(GitRepository gitRepository, string name) |
|||
{ |
|||
name += "_" + gitRepository.Name + "_" + gitRepository.BranchName; |
|||
|
|||
foreach (var dependingRepository in gitRepository.DependingRepositories) |
|||
{ |
|||
AddToUniqueName(dependingRepository, name); |
|||
} |
|||
} |
|||
|
|||
public string FindRepositoryOf(string csProjFilePath) |
|||
{ |
|||
if (csProjFilePath.StartsWith(RootPath)) |
|||
{ |
|||
return Name; |
|||
} |
|||
|
|||
foreach (var dependingRepository in DependingRepositories) |
|||
{ |
|||
var name = FindRepositoryOfInternal(dependingRepository, csProjFilePath); |
|||
if (!string.IsNullOrEmpty(name)) |
|||
{ |
|||
return name; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private string FindRepositoryOfInternal(GitRepository repository, string csProjFilePath) |
|||
{ |
|||
if (csProjFilePath.StartsWith(repository.RootPath)) |
|||
{ |
|||
return repository.Name; |
|||
} |
|||
|
|||
foreach (var dependingRepository in repository.DependingRepositories) |
|||
{ |
|||
return FindRepositoryOfInternal(dependingRepository, csProjFilePath); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public class GitRepositoryBuildStatus |
|||
{ |
|||
/// <summary>
|
|||
/// Name of the repository
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Branch of the repository
|
|||
/// </summary>
|
|||
public string BranchName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Last succeeded commitId of the repository
|
|||
/// </summary>
|
|||
public string CommitId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Build succeeded projects of this repository
|
|||
/// </summary>
|
|||
public List<DotNetProjectBuildStatus> SucceedProjects { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Build status of depending repositories
|
|||
/// </summary>
|
|||
public List<GitRepositoryBuildStatus> DependingRepositories { get; set; } |
|||
|
|||
public GitRepositoryBuildStatus(string name, string branchName) |
|||
{ |
|||
Name = name; |
|||
BranchName = branchName; |
|||
SucceedProjects = new List<DotNetProjectBuildStatus>(); |
|||
DependingRepositories = new List<GitRepositoryBuildStatus>(); |
|||
} |
|||
|
|||
public GitRepositoryBuildStatus GetChild(string repositoryName) |
|||
{ |
|||
foreach (var dependingRepository in DependingRepositories) |
|||
{ |
|||
return GetChildInternal(dependingRepository, repositoryName); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private GitRepositoryBuildStatus GetChildInternal(GitRepositoryBuildStatus repositoryBuildStatus, |
|||
string repositoryName) |
|||
{ |
|||
if (repositoryBuildStatus.Name == repositoryName) |
|||
{ |
|||
return repositoryBuildStatus; |
|||
} |
|||
|
|||
foreach (var dependingRepository in repositoryBuildStatus.DependingRepositories) |
|||
{ |
|||
return GetChildInternal(dependingRepository, repositoryName); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public string GetUniqueName() |
|||
{ |
|||
var name = Name + "_" + BranchName; |
|||
foreach (var dependingRepository in DependingRepositories) |
|||
{ |
|||
AddToUniqueName(dependingRepository, name); |
|||
} |
|||
|
|||
return name.ToMd5(); |
|||
} |
|||
|
|||
private void AddToUniqueName(GitRepositoryBuildStatus gitRepository, string name) |
|||
{ |
|||
name += "_" + gitRepository.Name + "_" + gitRepository.BranchName; |
|||
|
|||
foreach (var dependingRepository in gitRepository.DependingRepositories) |
|||
{ |
|||
AddToUniqueName(dependingRepository, name); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using LibGit2Sharp; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public interface IDotNetProjectBuildConfigReader |
|||
{ |
|||
DotNetProjectBuildConfig Read(string directoryPath); |
|||
} |
|||
|
|||
public class FileSystemDotNetProjectBuildConfigReader : IDotNetProjectBuildConfigReader |
|||
{ |
|||
private IJsonSerializer _jsonSerializer; |
|||
|
|||
public FileSystemDotNetProjectBuildConfigReader(IJsonSerializer jsonSerializer) |
|||
{ |
|||
_jsonSerializer = jsonSerializer; |
|||
} |
|||
|
|||
public DotNetProjectBuildConfig Read(string directoryPath) |
|||
{ |
|||
var buildArgs = new DotNetProjectBuildConfig(); |
|||
var solutionFiles = Directory.GetFiles(directoryPath, "*.sln", SearchOption.TopDirectoryOnly); |
|||
if (solutionFiles.Length == 1) |
|||
{ |
|||
buildArgs.SlFilePath = solutionFiles.First(); |
|||
} |
|||
else |
|||
{ |
|||
Console.WriteLine( |
|||
"There are more than 1 solution (*.sln) file in the directory. Searching for build-config.json file." |
|||
); |
|||
} |
|||
|
|||
var configFiles = Directory.GetFiles(directoryPath, "abp-build-config.json", SearchOption.TopDirectoryOnly); |
|||
if (configFiles.Length == 1) |
|||
{ |
|||
var configFile = configFiles.First(); |
|||
buildArgs.GitRepository = _jsonSerializer.Deserialize<GitRepository>(configFile); |
|||
} |
|||
else |
|||
{ |
|||
Console.WriteLine( |
|||
"There are more than 1 config (abp-build-config.json) file in the directory!" |
|||
); |
|||
} |
|||
|
|||
SetBranchNames(buildArgs.GitRepository); |
|||
|
|||
return buildArgs; |
|||
} |
|||
|
|||
private void SetBranchNames(GitRepository gitRepository) |
|||
{ |
|||
using (var repo = new Repository(string.Concat(gitRepository.RootPath, @"\.git"))) |
|||
{ |
|||
gitRepository.BranchName = repo.Head.FriendlyName; |
|||
} |
|||
|
|||
foreach (var dependingRepository in gitRepository.DependingRepositories) |
|||
{ |
|||
SetBranchNames(dependingRepository); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public interface IChangedProjectFinder |
|||
{ |
|||
List<DotNetProjectInfo> Find(DotNetProjectBuildConfig buildConfig); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public interface IDotNetProjectBuilder |
|||
{ |
|||
List<string> Build(List<DotNetProjectInfo> projects, int maxParallelBuildCount, string arguments); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public interface IDotNetProjectDependencyFiller |
|||
{ |
|||
void Fill(List<DotNetProjectInfo> projects); |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System.IO; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.IO; |
|||
|
|||
namespace Volo.Abp.Cli.Build |
|||
{ |
|||
public interface IRepositoryBuildStatusStore |
|||
{ |
|||
GitRepositoryBuildStatus Get(GitRepository repository); |
|||
|
|||
void Set(GitRepositoryBuildStatus status); |
|||
} |
|||
|
|||
public class FileSystemRepositoryBuildStatusStore : IRepositoryBuildStatusStore, ITransientDependency |
|||
{ |
|||
// TODO: change this ?
|
|||
private string BaseBuildStatusStorePath = @"C:\Users\ismai\.abp\build"; |
|||
|
|||
public GitRepositoryBuildStatus Get(GitRepository repository) |
|||
{ |
|||
if (!Directory.Exists(BaseBuildStatusStorePath)) |
|||
{ |
|||
Directory.CreateDirectory(BaseBuildStatusStorePath); |
|||
} |
|||
|
|||
var buildStatusFile = Path.Combine(BaseBuildStatusStorePath, repository.GetUniqueName()) + ".json"; |
|||
|
|||
if (!File.Exists(buildStatusFile)) |
|||
{ |
|||
// TODO: this is wrong
|
|||
return new GitRepositoryBuildStatus(repository.Name, repository.BranchName); |
|||
} |
|||
|
|||
var buildStatusText = File.ReadAllText(buildStatusFile); |
|||
return JsonConvert.DeserializeObject<GitRepositoryBuildStatus>(buildStatusText); |
|||
} |
|||
|
|||
public void Set(GitRepositoryBuildStatus status) |
|||
{ |
|||
if (!Directory.Exists(BaseBuildStatusStorePath)) |
|||
{ |
|||
Directory.CreateDirectory(BaseBuildStatusStorePath); |
|||
} |
|||
|
|||
var buildStatusFile = Path.Combine(BaseBuildStatusStorePath, status.GetUniqueName()) + ".json"; |
|||
if (File.Exists(buildStatusFile)) |
|||
{ |
|||
FileHelper.DeleteIfExists(buildStatusFile); |
|||
} |
|||
|
|||
using (var file = File.CreateText(buildStatusFile)) |
|||
{ |
|||
new JsonSerializer {Formatting = Formatting.Indented}.Serialize(file, status); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,180 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using LibGit2Sharp; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.Cli.Args; |
|||
using Volo.Abp.Cli.Build; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Cli.Commands |
|||
{ |
|||
public class BuildCommand : IConsoleCommand, ITransientDependency |
|||
{ |
|||
public ILogger<BuildCommand> Logger { get; set; } |
|||
|
|||
public IDotNetProjectDependencyFiller DotNetProjectDependencyFiller { get; set; } |
|||
|
|||
public IChangedProjectFinder ChangedProjectFinder { get; set; } |
|||
|
|||
public IDotNetProjectBuilder DotNetProjectBuilder { get; set; } |
|||
|
|||
public IRepositoryBuildStatusStore RepositoryBuildStatusStore { get; set; } |
|||
|
|||
public IDotNetProjectBuildConfigReader DotNetProjectBuildConfigReader { get; set; } |
|||
|
|||
public Task ExecuteAsync(CommandLineArgs commandLineArgs) |
|||
{ |
|||
var sw = new Stopwatch(); |
|||
sw.Start(); |
|||
|
|||
var buildConfig = DotNetProjectBuildConfigReader.Read(Directory.GetCurrentDirectory()); |
|||
|
|||
var maxParallelBuild = commandLineArgs.Options.GetOrNull( |
|||
Options.MaxParallelBuild.Short, |
|||
Options.MaxParallelBuild.Long |
|||
); |
|||
|
|||
var dotnetBuildArguments = commandLineArgs.Options.GetOrNull(Options.DotnetBuildArguments.Short, |
|||
Options.DotnetBuildArguments.Long); |
|||
|
|||
var changedProjectFiles = ChangedProjectFinder.Find(buildConfig); |
|||
|
|||
DotNetProjectDependencyFiller.Fill(changedProjectFiles); |
|||
|
|||
var sortedProjects = changedProjectFiles.SortByDependencies(p => p.Dependencies); |
|||
|
|||
var buildSucceededProjects = DotNetProjectBuilder.Build( |
|||
sortedProjects, |
|||
string.IsNullOrEmpty(maxParallelBuild) ? 1 : Convert.ToInt32(maxParallelBuild), |
|||
dotnetBuildArguments ?? "" |
|||
); |
|||
|
|||
var buildStatus = GenerateBuildStatus(buildConfig.GitRepository, changedProjectFiles, buildSucceededProjects); |
|||
RepositoryBuildStatusStore.Set(buildStatus); |
|||
|
|||
sw.Stop(); |
|||
Console.WriteLine("Build operation is completed in " + sw.ElapsedMilliseconds + " (ms)"); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
private GitRepositoryBuildStatus GenerateBuildStatus( |
|||
GitRepository gitRepository, |
|||
List<DotNetProjectInfo> changedProjects, |
|||
List<string> buildSucceededProjects) |
|||
{ |
|||
using (var repo = new Repository(string.Concat(gitRepository.RootPath, @"\.git"))) |
|||
{ |
|||
var lastCommitId = repo.Head.Tip.Id.ToString(); |
|||
var status = new GitRepositoryBuildStatus( |
|||
gitRepository.Name, |
|||
repo.Head.FriendlyName |
|||
) |
|||
{ |
|||
CommitId = lastCommitId |
|||
}; |
|||
|
|||
status.SucceedProjects = changedProjects.Where(p => |
|||
p.RepositoryName == gitRepository.Name && |
|||
buildSucceededProjects.Contains(p.CsProjPath) |
|||
) |
|||
.Select(e => new DotNetProjectBuildStatus() |
|||
{ |
|||
CsProjPath = e.CsProjPath, |
|||
CommitId = lastCommitId |
|||
}).ToList(); |
|||
|
|||
foreach (var dependingRepository in gitRepository.DependingRepositories) |
|||
{ |
|||
GenerateBuildStatusInternal(dependingRepository, changedProjects, buildSucceededProjects, status); |
|||
} |
|||
|
|||
return status; |
|||
} |
|||
} |
|||
|
|||
private void GenerateBuildStatusInternal( |
|||
GitRepository gitRepository, |
|||
List<DotNetProjectInfo> changedProjects, |
|||
List<string> buildSucceededProjects, |
|||
GitRepositoryBuildStatus status) |
|||
{ |
|||
using (var repo = new Repository(string.Concat(gitRepository.RootPath, @"\.git"))) |
|||
{ |
|||
var lastCommitId = repo.Head.Tip.Id.ToString(); |
|||
var dependingRepositoryStatus = new GitRepositoryBuildStatus( |
|||
gitRepository.Name, |
|||
repo.Head.FriendlyName |
|||
) |
|||
{ |
|||
CommitId = lastCommitId |
|||
}; |
|||
|
|||
dependingRepositoryStatus.SucceedProjects = changedProjects.Where(p => |
|||
p.RepositoryName == gitRepository.Name && |
|||
buildSucceededProjects.Contains(p.CsProjPath) |
|||
) |
|||
.Select(e => new DotNetProjectBuildStatus() |
|||
{ |
|||
CsProjPath = e.CsProjPath, |
|||
CommitId = lastCommitId |
|||
}).ToList(); |
|||
|
|||
foreach (var dependingRepository in gitRepository.DependingRepositories) |
|||
{ |
|||
GenerateBuildStatusInternal( |
|||
dependingRepository, |
|||
changedProjects, |
|||
buildSucceededProjects, |
|||
dependingRepositoryStatus |
|||
); |
|||
} |
|||
|
|||
status.DependingRepositories.Add(dependingRepositoryStatus); |
|||
} |
|||
} |
|||
|
|||
public string GetUsageInfo() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Usage:"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine(" abp build [options]"); |
|||
sb.AppendLine(""); |
|||
sb.AppendLine("Options:"); |
|||
sb.AppendLine(""); |
|||
// TODO: Explain extra parameters
|
|||
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 "Builds a dotnet repository and dependent repositories or a solution."; |
|||
} |
|||
|
|||
public static class Options |
|||
{ |
|||
public static class MaxParallelBuild |
|||
{ |
|||
public const string Short = "m"; |
|||
public const string Long = "max-paralel-builds"; |
|||
} |
|||
|
|||
public static class DotnetBuildArguments |
|||
{ |
|||
public const string Short = "d"; |
|||
public const string Long = "dotnet-build-arguments"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue