mirror of https://github.com/abpframework/abp.git
Browse Source
Introduced "abp build" command to optimize building for big mono repositoriespull/5443/head
committed by
GitHub
35 changed files with 2071 additions and 4 deletions
@ -0,0 +1,67 @@ |
|||||
|
# Build Command |
||||
|
|
||||
|
Building a .NET project is hard when the project references a project reference outside of the solution or even from a different GIT repository. This command builds a GIT repository and it's depending repositories or a single .NET solution File. In order ```build``` command to work, its **executing directory** or passed ```--working-directory``` parameter's directory must contain one of; |
||||
|
|
||||
|
* A .NET solution file (*.sln) |
||||
|
* abp-build-config.json |
||||
|
|
||||
|
When the executing directory (or ```--working-directory``` parameter's directory) contains a .NET solution file, ```build``` command builds all the projects in the related solution file and all project references on building project files recursively. |
||||
|
|
||||
|
When the executing directory (or ```--working-directory``` parameter's directory) contains a ```abp-build-config.json```, ```build``` command builds all changed projects form its last build and all project references on building project files recursively. |
||||
|
|
||||
|
# Build Command Config |
||||
|
|
||||
|
```abp-build-config.json``` contains properties below; |
||||
|
|
||||
|
* ```Name```: Name of the GIT repository. This can be friendly name of your GIT repository or any other unique string for the repository. |
||||
|
* ```RootPath```: Root path of the repository which contains ```.git``` folder. |
||||
|
* ```DependingRepositories```: Depending repository list of a repository. Each depending repository item contains same fields as a repository. |
||||
|
* ```IgnoredDirectories```: Relative directory paths to ignore while building a GIT repository. |
||||
|
|
||||
|
A sample ```abp-build-config.json``` looks like for a Windows OS; |
||||
|
|
||||
|
````json |
||||
|
{ |
||||
|
"Name": "main-repository", |
||||
|
"RootPath": "D:\\GitHub\\main-repository", |
||||
|
"DependingRepositories": [{ |
||||
|
"Name": "module-repository", |
||||
|
"RootPath": "D:\\GitHub\\module-repository" |
||||
|
}], |
||||
|
"IgnoredDirectories": [ |
||||
|
"utils" |
||||
|
] |
||||
|
} |
||||
|
```` |
||||
|
|
||||
|
# Build Status |
||||
|
|
||||
|
ABP CLI stores a build status file for builds using the repository friendly names and current branch names in the; |
||||
|
|
||||
|
* ```%USERPROFILE%\.abp\build\``` for Windows. |
||||
|
* ```$HOME/.abp/build/``` for Linux/macOS. |
||||
|
|
||||
|
and uses this file when building same repository next time and only builds affected projects and decreases the total build time. A sample build status file content looks like; |
||||
|
|
||||
|
````json |
||||
|
{ |
||||
|
"RepositoryName": "main-repository", |
||||
|
"BranchName": "dev", |
||||
|
"CommitId": "84ecde8ba275aeeb14d24a87ad46a1e941adf8ba", |
||||
|
"SucceedProjects": [{ |
||||
|
"CsProjPath": "D:\\GitHub\\main-repository\\BookStore\\BookStore.Web.csproj", |
||||
|
"CommitId": "84ecde8ba275aeeb14d24a87ad46a1e941adf8ba" |
||||
|
}], |
||||
|
"DependingRepositories": [{ |
||||
|
"RepositoryName": "module-repository", |
||||
|
"BranchName": "dev", |
||||
|
"CommitId": "0598b8e45af9507fc9ba8abf304e78fc7d434e04", |
||||
|
"SucceedProjects": [{ |
||||
|
"CsProjPath": "D:\\GitHub\\module-repository\\identity-module\Identity\\Identity.Web.csproj", |
||||
|
"CommitId": "0598b8e45af9507fc9ba8abf304e78fc7d434e04" |
||||
|
}], |
||||
|
"DependingRepositories": [] |
||||
|
}] |
||||
|
} |
||||
|
```` |
||||
|
|
||||
@ -0,0 +1,67 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class DefaultBuildProjectListSorter : IBuildProjectListSorter, ITransientDependency |
||||
|
{ |
||||
|
public List<DotNetProjectInfo> SortByDependencies( |
||||
|
List<DotNetProjectInfo> source, |
||||
|
IEqualityComparer<DotNetProjectInfo> comparer = null) |
||||
|
{ |
||||
|
/* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp
|
||||
|
* http://en.wikipedia.org/wiki/Topological_sorting
|
||||
|
*/ |
||||
|
|
||||
|
var sorted = new List<DotNetProjectInfo>(); |
||||
|
var visited = new Dictionary<DotNetProjectInfo, bool>(comparer); |
||||
|
|
||||
|
foreach (var item in source) |
||||
|
{ |
||||
|
SortByDependenciesVisit(source, item, sorted, visited); |
||||
|
} |
||||
|
|
||||
|
return sorted; |
||||
|
} |
||||
|
|
||||
|
private void SortByDependenciesVisit( |
||||
|
List<DotNetProjectInfo> source, |
||||
|
DotNetProjectInfo item, |
||||
|
List<DotNetProjectInfo> sorted, |
||||
|
Dictionary<DotNetProjectInfo, bool> visited) |
||||
|
{ |
||||
|
bool inProcess; |
||||
|
var alreadyVisited = visited.TryGetValue(item, out inProcess); |
||||
|
|
||||
|
if (alreadyVisited) |
||||
|
{ |
||||
|
if (inProcess) |
||||
|
{ |
||||
|
throw new ArgumentException("Cyclic dependency found! Item: " + item); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
visited[item] = true; |
||||
|
|
||||
|
var dependencies = item.Dependencies; |
||||
|
if (dependencies != null) |
||||
|
{ |
||||
|
foreach (var dependency in dependencies) |
||||
|
{ |
||||
|
var dependencyItem = source.FirstOrDefault(e => e.CsProjPath == dependency.CsProjPath); |
||||
|
if (dependencyItem != null) |
||||
|
{ |
||||
|
SortByDependenciesVisit(source, dependencyItem, sorted, visited); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
visited[item] = false; |
||||
|
sorted.Add(item); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using LibGit2Sharp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class DefaultBuildStatusGenerator : IBuildStatusGenerator, ITransientDependency |
||||
|
{ |
||||
|
private readonly IGitRepositoryHelper _gitRepositoryHelper; |
||||
|
|
||||
|
public DefaultBuildStatusGenerator(IGitRepositoryHelper gitRepositoryHelper) |
||||
|
{ |
||||
|
_gitRepositoryHelper = gitRepositoryHelper; |
||||
|
} |
||||
|
|
||||
|
public GitRepositoryBuildStatus Generate( |
||||
|
DotNetProjectBuildConfig buildConfig, |
||||
|
List<DotNetProjectInfo> changedProjects, |
||||
|
List<string> buildSucceededProjects) |
||||
|
{ |
||||
|
var lastCommitId = _gitRepositoryHelper.GetLastCommitId(buildConfig.GitRepository); |
||||
|
var repoFriendlyName = _gitRepositoryHelper.GetFriendlyName(buildConfig.GitRepository); |
||||
|
|
||||
|
var status = new GitRepositoryBuildStatus( |
||||
|
buildConfig.GitRepository.Name, |
||||
|
repoFriendlyName |
||||
|
); |
||||
|
|
||||
|
if (ShouldUpdateRepositoryCommitId(buildConfig, changedProjects, buildSucceededProjects)) |
||||
|
{ |
||||
|
status.CommitId = lastCommitId; |
||||
|
} |
||||
|
|
||||
|
status.SucceedProjects = changedProjects.Where(p => |
||||
|
p.RepositoryName == buildConfig.GitRepository.Name && |
||||
|
buildSucceededProjects.Contains(p.CsProjPath) |
||||
|
) |
||||
|
.Select(e => new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = e.CsProjPath, |
||||
|
CommitId = lastCommitId |
||||
|
}).ToList(); |
||||
|
|
||||
|
foreach (var dependingRepository in buildConfig.GitRepository.DependingRepositories) |
||||
|
{ |
||||
|
GenerateBuildStatusInternal( |
||||
|
buildConfig, |
||||
|
dependingRepository, |
||||
|
changedProjects, |
||||
|
buildSucceededProjects, |
||||
|
status |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return status; |
||||
|
} |
||||
|
|
||||
|
private bool ShouldUpdateRepositoryCommitId( |
||||
|
DotNetProjectBuildConfig buildConfig, |
||||
|
List<DotNetProjectInfo> changedProjects, |
||||
|
List<string> buildSucceededProjects) |
||||
|
{ |
||||
|
if (!buildConfig.SlFilePath.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (changedProjects.Count == 0 || buildSucceededProjects.Count == 0) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return changedProjects.Count == buildSucceededProjects.Count; |
||||
|
} |
||||
|
|
||||
|
private void GenerateBuildStatusInternal( |
||||
|
DotNetProjectBuildConfig buildConfig, |
||||
|
GitRepository gitRepository, |
||||
|
List<DotNetProjectInfo> changedProjects, |
||||
|
List<string> buildSucceededProjects, |
||||
|
GitRepositoryBuildStatus status) |
||||
|
{ |
||||
|
var lastCommitId = _gitRepositoryHelper.GetLastCommitId(gitRepository); |
||||
|
var repoFriendlyName = _gitRepositoryHelper.GetFriendlyName(gitRepository); |
||||
|
|
||||
|
var dependingRepositoryStatus = new GitRepositoryBuildStatus( |
||||
|
gitRepository.Name, |
||||
|
repoFriendlyName |
||||
|
); |
||||
|
|
||||
|
if (ShouldUpdateRepositoryCommitId(buildConfig, changedProjects, buildSucceededProjects)) |
||||
|
{ |
||||
|
dependingRepositoryStatus.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( |
||||
|
buildConfig, |
||||
|
dependingRepository, |
||||
|
changedProjects, |
||||
|
buildSucceededProjects, |
||||
|
dependingRepositoryStatus |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
status.DependingRepositories.Add(dependingRepositoryStatus); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,352 @@ |
|||||
|
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; |
||||
|
private readonly IGitRepositoryHelper _gitRepositoryHelper; |
||||
|
|
||||
|
private readonly IDotNetProjectDependencyFiller _dotNetProjectDependencyFiller; |
||||
|
private readonly IBuildProjectListSorter _buildProjectListSorter; |
||||
|
|
||||
|
private readonly List<string> _changeDetectionFileExtensions = new List<string> |
||||
|
{ |
||||
|
".cs", |
||||
|
".csproj", |
||||
|
".cshtml" |
||||
|
}; |
||||
|
|
||||
|
public DefaultChangedProjectFinder( |
||||
|
IRepositoryBuildStatusStore repositoryBuildStatusStore, |
||||
|
IGitRepositoryHelper gitRepositoryHelper, |
||||
|
IDotNetProjectDependencyFiller dotNetProjectDependencyFiller, |
||||
|
IBuildProjectListSorter buildProjectListSorter) |
||||
|
{ |
||||
|
_repositoryBuildStatusStore = repositoryBuildStatusStore; |
||||
|
_gitRepositoryHelper = gitRepositoryHelper; |
||||
|
_dotNetProjectDependencyFiller = dotNetProjectDependencyFiller; |
||||
|
_buildProjectListSorter = buildProjectListSorter; |
||||
|
} |
||||
|
|
||||
|
public List<DotNetProjectInfo> Find(DotNetProjectBuildConfig buildConfig) |
||||
|
{ |
||||
|
if (!buildConfig.SlFilePath.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return FindBySlnFile(buildConfig); |
||||
|
} |
||||
|
|
||||
|
return FindByRepository(buildConfig); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns list of projects in a repository and its depending repositories sorted by dependencies
|
||||
|
/// </summary>
|
||||
|
/// <param name="buildConfig"></param>
|
||||
|
/// <returns></returns>
|
||||
|
private List<DotNetProjectInfo> FindAllProjects(DotNetProjectBuildConfig buildConfig) |
||||
|
{ |
||||
|
var projects = new List<DotNetProjectInfo>(); |
||||
|
|
||||
|
AddProjectsOfRepository(buildConfig.GitRepository, projects); |
||||
|
|
||||
|
_dotNetProjectDependencyFiller.Fill(projects); |
||||
|
|
||||
|
var allSortedProjectList = _buildProjectListSorter.SortByDependencies( |
||||
|
projects, |
||||
|
new DotNetProjectInfoEqualityComparer() |
||||
|
); |
||||
|
|
||||
|
FilterIgnoredDirectories(allSortedProjectList, buildConfig.GitRepository); |
||||
|
|
||||
|
return allSortedProjectList; |
||||
|
} |
||||
|
|
||||
|
private void FilterIgnoredDirectories(List<DotNetProjectInfo> projects, GitRepository gitRepository) |
||||
|
{ |
||||
|
foreach (var ignoredDirectory in gitRepository.IgnoredDirectories) |
||||
|
{ |
||||
|
projects = projects.Where(e => |
||||
|
!e.CsProjPath.StartsWith(Path.Combine(gitRepository.RootPath, ignoredDirectory))) |
||||
|
.ToList(); |
||||
|
} |
||||
|
|
||||
|
foreach (var dependingRepository in gitRepository.DependingRepositories) |
||||
|
{ |
||||
|
FilterIgnoredDirectories(projects, dependingRepository); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void AddProjectsOfRepository(GitRepository gitRepository, List<DotNetProjectInfo> projects) |
||||
|
{ |
||||
|
var allCsProjFiles = Directory.GetFiles( |
||||
|
gitRepository.RootPath, |
||||
|
"*.csproj", |
||||
|
SearchOption.AllDirectories |
||||
|
).ToList(); |
||||
|
|
||||
|
projects.AddRange( |
||||
|
allCsProjFiles.Select(csProjPath => new DotNetProjectInfo(gitRepository.Name, csProjPath, false)) |
||||
|
); |
||||
|
|
||||
|
foreach (var dependingRepository in gitRepository.DependingRepositories) |
||||
|
{ |
||||
|
AddProjectsOfRepository(dependingRepository, projects); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<DotNetProjectInfo> FindByRepository(DotNetProjectBuildConfig buildConfig) |
||||
|
{ |
||||
|
var gitRepositoryBuildStatus = _repositoryBuildStatusStore.Get( |
||||
|
buildConfig.BuildName, |
||||
|
buildConfig.GitRepository |
||||
|
); |
||||
|
|
||||
|
var allSortedProjectList = FindAllProjects(buildConfig); |
||||
|
|
||||
|
MarkProjectsForBuild( |
||||
|
buildConfig.GitRepository, |
||||
|
gitRepositoryBuildStatus, |
||||
|
buildConfig.ForceBuild, |
||||
|
allSortedProjectList |
||||
|
); |
||||
|
|
||||
|
return allSortedProjectList.Where(e => e.ShouldBuild).ToList(); |
||||
|
} |
||||
|
|
||||
|
private void MarkProjectsForBuild( |
||||
|
GitRepository repository, |
||||
|
GitRepositoryBuildStatus repositoryBuildStatus, |
||||
|
bool forceBuild, |
||||
|
List<DotNetProjectInfo> allProjectList) |
||||
|
{ |
||||
|
if (forceBuild || repositoryBuildStatus == null || repositoryBuildStatus.CommitId.IsNullOrEmpty()) |
||||
|
{ |
||||
|
// Mark all projects for build
|
||||
|
allProjectList.ForEach(e => e.ShouldBuild = true); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
MarkChangedProjectsForBuild( |
||||
|
repository, |
||||
|
repositoryBuildStatus, |
||||
|
allProjectList |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
if (!repository.DependingRepositories.Any()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var dependingRepository in repository.DependingRepositories) |
||||
|
{ |
||||
|
var dependingRepositoryBuildStatus = repositoryBuildStatus?.GetChild(dependingRepository.Name); |
||||
|
MarkProjectsForBuild( |
||||
|
dependingRepository, |
||||
|
dependingRepositoryBuildStatus, |
||||
|
forceBuild, |
||||
|
allProjectList |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void MarkChangedProjectsForBuild( |
||||
|
GitRepository repository, |
||||
|
GitRepositoryBuildStatus status, |
||||
|
List<DotNetProjectInfo> allProjectList) |
||||
|
{ |
||||
|
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 fileExtensionPredicate = PredicateBuilder.New<PatchEntryChanges>(true); |
||||
|
|
||||
|
foreach (var changeDetectionFileExtension in _changeDetectionFileExtensions) |
||||
|
{ |
||||
|
fileExtensionPredicate = fileExtensionPredicate.Or( |
||||
|
e => e.Path.EndsWith(changeDetectionFileExtension) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
var files = repoDifferences |
||||
|
.Where(fileExtensionPredicate) |
||||
|
.Where(e => e.Status != ChangeKind.Deleted) |
||||
|
.Select(e => e) |
||||
|
.ToList(); |
||||
|
|
||||
|
var affectedCsProjFiles = FindAffectedCsProjFiles(repository.RootPath, files); |
||||
|
var lastCommitId = _gitRepositoryHelper.GetLastCommitId(repository); |
||||
|
|
||||
|
foreach (var file in affectedCsProjFiles) |
||||
|
{ |
||||
|
var csProjPath = Path.Combine(repository.RootPath, file); |
||||
|
if (status.SucceedProjects.Any(p => p.CsProjPath == csProjPath && p.CommitId == lastCommitId)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// Filter ignored directories
|
||||
|
var isIgnored = repository.IgnoredDirectories.Any(ignoredDirectory => |
||||
|
csProjPath.StartsWith(Path.Combine(repository.RootPath, ignoredDirectory))); |
||||
|
if (isIgnored) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
allProjectList.MarkForBuild(repository.Name, csProjPath); |
||||
|
AddDependingProjectsToList(repository.Name, csProjPath, allProjectList); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<DotNetProjectInfo> FindBySlnFile(DotNetProjectBuildConfig buildConfig) |
||||
|
{ |
||||
|
var allProjectList = FindAllProjects(buildConfig); |
||||
|
|
||||
|
var slFine = new FileInfo(buildConfig.SlFilePath); |
||||
|
var csProjFiles = slFine.Directory.GetFiles( |
||||
|
"*.csproj", |
||||
|
SearchOption.AllDirectories |
||||
|
).Select(e => e.FullName) |
||||
|
.ToList(); |
||||
|
|
||||
|
foreach (var csProjFile in csProjFiles) |
||||
|
{ |
||||
|
MarkDependantProjectsForBuild(buildConfig.GitRepository, csProjFile, allProjectList); |
||||
|
} |
||||
|
|
||||
|
return _buildProjectListSorter.SortByDependencies( |
||||
|
allProjectList, |
||||
|
new DotNetProjectInfoEqualityComparer() |
||||
|
).Where(e => e.ShouldBuild).ToList(); |
||||
|
} |
||||
|
|
||||
|
private void MarkDependantProjectsForBuild( |
||||
|
GitRepository gitRepository, |
||||
|
string csProjFilePath, |
||||
|
List<DotNetProjectInfo> allProjectList) |
||||
|
{ |
||||
|
var repositoryName = gitRepository.FindRepositoryOf(csProjFilePath); |
||||
|
var project = new DotNetProjectInfo(repositoryName, csProjFilePath, true); |
||||
|
|
||||
|
if (allProjectList.IsMarkedForBuild(repositoryName, csProjFilePath)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
allProjectList.MarkForBuild(project); |
||||
|
AddProjectDependencies(gitRepository, project, allProjectList); |
||||
|
} |
||||
|
|
||||
|
private void AddProjectDependencies( |
||||
|
GitRepository gitRepository, |
||||
|
DotNetProjectInfo project, |
||||
|
List<DotNetProjectInfo> allProjectList) |
||||
|
{ |
||||
|
var projectInfo = allProjectList.FirstOrDefault(e => e.CsProjPath == project.CsProjPath); |
||||
|
if (projectInfo == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var dependencies = projectInfo.Dependencies; |
||||
|
|
||||
|
foreach (var dependency in dependencies) |
||||
|
{ |
||||
|
if (allProjectList.IsMarkedForBuild(dependency.RepositoryName, dependency.CsProjPath)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
allProjectList.MarkForBuild(dependency.RepositoryName, dependency.CsProjPath); |
||||
|
|
||||
|
AddProjectDependencies(gitRepository, dependency, allProjectList); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void AddDependingProjectsToList( |
||||
|
string repositoryName, |
||||
|
string csProjPath, |
||||
|
List<DotNetProjectInfo> allProjectList) |
||||
|
{ |
||||
|
var dependingProjects = allProjectList.Where( |
||||
|
e => e.Dependencies.Any(d => d.RepositoryName == repositoryName && d.CsProjPath == csProjPath) |
||||
|
).Select(e => new DotNetProjectInfo(e.RepositoryName, e.CsProjPath, true)).ToList(); |
||||
|
|
||||
|
if (!dependingProjects.Any()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var dependingProject in dependingProjects) |
||||
|
{ |
||||
|
if (allProjectList.IsMarkedForBuild(dependingProject)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
allProjectList.MarkForBuild(dependingProject); |
||||
|
AddDependingProjectsToList(dependingProject.RepositoryName, dependingProject.CsProjPath, |
||||
|
allProjectList); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<string> FindAffectedCsProjFiles(string repositoryPath, List<PatchEntryChanges> files) |
||||
|
{ |
||||
|
var affectedProjectFiles = new List<string>(); |
||||
|
foreach (var file in files) |
||||
|
{ |
||||
|
var filePath = Path.Combine(repositoryPath, file.Path); |
||||
|
if (filePath.EndsWith(".csproj")) |
||||
|
{ |
||||
|
affectedProjectFiles.Add(filePath); |
||||
|
} |
||||
|
|
||||
|
if (!filePath.EndsWith(".cs")) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var classFile = new FileInfo(filePath); |
||||
|
var csProjPath = FindBelongingProjectPathOfClass(classFile.Directory?.FullName); |
||||
|
if (csProjPath.IsNullOrEmpty() || affectedProjectFiles.Contains(csProjPath)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
affectedProjectFiles.Add(csProjPath); |
||||
|
} |
||||
|
|
||||
|
return affectedProjectFiles; |
||||
|
} |
||||
|
|
||||
|
private string FindBelongingProjectPathOfClass(string directoryPath) |
||||
|
{ |
||||
|
var files = Directory.GetFiles(directoryPath, "*.csproj", SearchOption.TopDirectoryOnly); |
||||
|
if (files.Length == 1) |
||||
|
{ |
||||
|
return files.First(); |
||||
|
} |
||||
|
|
||||
|
var directoryInfo = new DirectoryInfo(directoryPath); |
||||
|
|
||||
|
if (directoryInfo.Parent == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return FindBelongingProjectPathOfClass(directoryInfo.Parent.FullName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
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, string arguments) |
||||
|
{ |
||||
|
var builtProjects = new ConcurrentBag<string>(); |
||||
|
var totalProjectCountToBuild = projects.Count; |
||||
|
var buildingProjectIndex = 0; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
foreach (var project in projects) |
||||
|
{ |
||||
|
if (builtProjects.Contains(project.CsProjPath)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
buildingProjectIndex++; |
||||
|
|
||||
|
Console.WriteLine( |
||||
|
"Building....: " + " (" + buildingProjectIndex + "/" + |
||||
|
totalProjectCountToBuild + ")" + project.CsProjPath |
||||
|
); |
||||
|
|
||||
|
BuildInternal(project, arguments, builtProjects); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Console.WriteLine(e); |
||||
|
} |
||||
|
|
||||
|
return builtProjects.ToList(); |
||||
|
} |
||||
|
|
||||
|
private void BuildInternal(DotNetProjectInfo project, string arguments, ConcurrentBag<string> builtProjects) |
||||
|
{ |
||||
|
var buildArguments = arguments.TrimStart('"').TrimEnd('"'); |
||||
|
Console.WriteLine("Executing...: dotnet build " + project.CsProjPath + " " + buildArguments); |
||||
|
|
||||
|
var output = CmdHelper.RunCmdAndGetOutput( |
||||
|
"dotnet build " + project.CsProjPath + " " + buildArguments, |
||||
|
out int buildStatus |
||||
|
); |
||||
|
|
||||
|
if (buildStatus == 0) |
||||
|
{ |
||||
|
builtProjects.Add(project.CsProjPath); |
||||
|
WriteOutput(output, ConsoleColor.Green); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
WriteOutput(output, ConsoleColor.Red); |
||||
|
Console.WriteLine("Build failed for :" + project.CsProjPath); |
||||
|
throw new Exception("Build failed!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void WriteOutput(string text, ConsoleColor color) |
||||
|
{ |
||||
|
var currentConsoleColor = Console.ForegroundColor; |
||||
|
Console.ForegroundColor = color; |
||||
|
Console.WriteLine(text); |
||||
|
Console.ForegroundColor = currentConsoleColor; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class DotNetProjectBuildConfig |
||||
|
{ |
||||
|
public string BuildName { get; set; } |
||||
|
|
||||
|
public string SlFilePath { get; set; } |
||||
|
|
||||
|
public GitRepository GitRepository { get; set; } |
||||
|
|
||||
|
public bool ForceBuild { 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,41 @@ |
|||||
|
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 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, false); |
||||
|
project.Dependencies.Add(referenceProject); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
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 bool ShouldBuild { get; set; } |
||||
|
|
||||
|
public List<DotNetProjectInfo> Dependencies { get; set; } |
||||
|
|
||||
|
public DotNetProjectInfo(string repositoryName, string csProjPath, bool shouldBuild) |
||||
|
{ |
||||
|
RepositoryName = repositoryName; |
||||
|
CsProjPath = csProjPath; |
||||
|
ShouldBuild = shouldBuild; |
||||
|
Dependencies = new List<DotNetProjectInfo>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class DotNetProjectInfoEqualityComparer : EqualityComparer<DotNetProjectInfo> |
||||
|
{ |
||||
|
public override bool Equals(DotNetProjectInfo x, DotNetProjectInfo y) |
||||
|
{ |
||||
|
return (x == null && y == null) || (x != null && y != null && x.CsProjPath == y.CsProjPath); |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode(DotNetProjectInfo obj) |
||||
|
{ |
||||
|
return obj == null ? 0 : obj.CsProjPath.GetHashCode(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public static class DotNetProjectInfoExtensions |
||||
|
{ |
||||
|
public static void MarkForBuild(this List<DotNetProjectInfo> projects, string repositoryName, string csProjPath) |
||||
|
{ |
||||
|
var project = projects.FirstOrDefault(e => |
||||
|
e.RepositoryName == repositoryName && e.CsProjPath == csProjPath |
||||
|
); |
||||
|
|
||||
|
if (project == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
project.ShouldBuild = true; |
||||
|
} |
||||
|
|
||||
|
public static void MarkForBuild(this List<DotNetProjectInfo> projects, DotNetProjectInfo project) |
||||
|
{ |
||||
|
projects.MarkForBuild(project.RepositoryName, project.CsProjPath); |
||||
|
} |
||||
|
|
||||
|
public static bool IsMarkedForBuild(this List<DotNetProjectInfo> projects, string repositoryName, |
||||
|
string csProjPath) |
||||
|
{ |
||||
|
var project = projects.FirstOrDefault(e => |
||||
|
e.RepositoryName == repositoryName && e.CsProjPath == csProjPath |
||||
|
); |
||||
|
|
||||
|
if (project == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return project.ShouldBuild; |
||||
|
} |
||||
|
|
||||
|
public static bool IsMarkedForBuild(this List<DotNetProjectInfo> projects, DotNetProjectInfo project) |
||||
|
{ |
||||
|
return projects.IsMarkedForBuild(project.RepositoryName, project.CsProjPath); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using LibGit2Sharp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Json; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class FileSystemDotNetProjectBuildConfigReader : IDotNetProjectBuildConfigReader, ITransientDependency |
||||
|
{ |
||||
|
private readonly IJsonSerializer _jsonSerializer; |
||||
|
private string _buildConfigName = "abp-build-config.json"; |
||||
|
|
||||
|
public FileSystemDotNetProjectBuildConfigReader(IJsonSerializer jsonSerializer) |
||||
|
{ |
||||
|
_jsonSerializer = jsonSerializer; |
||||
|
} |
||||
|
|
||||
|
public DotNetProjectBuildConfig Read(string directoryPath) |
||||
|
{ |
||||
|
var buildConfig = new DotNetProjectBuildConfig(); |
||||
|
var solutionFiles = Directory.GetFiles(directoryPath, "*.sln", SearchOption.TopDirectoryOnly); |
||||
|
if (solutionFiles.Length == 1) |
||||
|
{ |
||||
|
buildConfig.SlFilePath = solutionFiles.First(); |
||||
|
var configFile = GetClosestFile(directoryPath, _buildConfigName); |
||||
|
var configFileContent = File.ReadAllText(configFile); |
||||
|
buildConfig.GitRepository = _jsonSerializer.Deserialize<GitRepository>(configFileContent); |
||||
|
|
||||
|
SetBranchNames(buildConfig.GitRepository); |
||||
|
|
||||
|
return buildConfig; |
||||
|
} |
||||
|
|
||||
|
var configFiles = Directory.GetFiles(directoryPath, _buildConfigName, SearchOption.TopDirectoryOnly); |
||||
|
if (configFiles.Length == 1) |
||||
|
{ |
||||
|
var configFile = configFiles.First(); |
||||
|
var configFileContent = File.ReadAllText(configFile); |
||||
|
buildConfig.GitRepository = _jsonSerializer.Deserialize<GitRepository>(configFileContent); |
||||
|
|
||||
|
SetBranchNames(buildConfig.GitRepository); |
||||
|
|
||||
|
return buildConfig; |
||||
|
} |
||||
|
|
||||
|
Console.WriteLine( |
||||
|
"There are more than 1 config (abp-build-config.json) file in the directory!" |
||||
|
); |
||||
|
|
||||
|
throw new Exception("There is no solution file (*.sln) or " + _buildConfigName + " in the working directory !"); |
||||
|
} |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private string GetClosestFile(string directoryPath, string fileName) |
||||
|
{ |
||||
|
var directory = new DirectoryInfo(directoryPath); |
||||
|
var files = directory.GetFiles(fileName, SearchOption.TopDirectoryOnly); |
||||
|
if (files.Any() && files.Length == 1) |
||||
|
{ |
||||
|
return files.First().FullName; |
||||
|
} |
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
directory = directory.Parent; |
||||
|
if (directory == null) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
files = directory.GetFiles(fileName, SearchOption.TopDirectoryOnly); |
||||
|
if (files.Any() && files.Length == 1) |
||||
|
{ |
||||
|
return files.First().FullName; |
||||
|
} |
||||
|
} while (true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
using System.IO; |
||||
|
using Newtonsoft.Json; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.IO; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class FileSystemRepositoryBuildStatusStore : IRepositoryBuildStatusStore, ITransientDependency |
||||
|
{ |
||||
|
public GitRepositoryBuildStatus Get(string buildNamePrefix, GitRepository repository) |
||||
|
{ |
||||
|
if (!Directory.Exists(CliPaths.Build)) |
||||
|
{ |
||||
|
Directory.CreateDirectory(CliPaths.Build); |
||||
|
} |
||||
|
|
||||
|
var buildStatusFile = Path.Combine(CliPaths.Build, repository.GetUniqueName(buildNamePrefix)) + |
||||
|
".json"; |
||||
|
|
||||
|
if (!File.Exists(buildStatusFile)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var buildStatusText = File.ReadAllText(buildStatusFile); |
||||
|
return JsonConvert.DeserializeObject<GitRepositoryBuildStatus>(buildStatusText); |
||||
|
} |
||||
|
|
||||
|
public void Set(string buildNamePrefix, GitRepository repository, GitRepositoryBuildStatus status) |
||||
|
{ |
||||
|
var existingRepositoryStatus = Get(buildNamePrefix, repository); |
||||
|
|
||||
|
var buildStatusFile = Path.Combine( |
||||
|
CliPaths.Build, |
||||
|
status.GetUniqueName(buildNamePrefix) |
||||
|
) + ".json"; |
||||
|
|
||||
|
if (File.Exists(buildStatusFile)) |
||||
|
{ |
||||
|
FileHelper.DeleteIfExists(buildStatusFile); |
||||
|
} |
||||
|
|
||||
|
if (existingRepositoryStatus != null) |
||||
|
{ |
||||
|
existingRepositoryStatus.MergeWith(status); |
||||
|
|
||||
|
using (var file = File.CreateText(buildStatusFile)) |
||||
|
{ |
||||
|
new JsonSerializer {Formatting = Formatting.Indented}.Serialize(file, existingRepositoryStatus); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
using (var file = File.CreateText(buildStatusFile)) |
||||
|
{ |
||||
|
new JsonSerializer {Formatting = Formatting.Indented}.Serialize(file, status); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,94 @@ |
|||||
|
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 List<string> IgnoredDirectories { get; set; } |
||||
|
|
||||
|
public GitRepository(string name, string branchName, string rootPath) |
||||
|
{ |
||||
|
Name = name; |
||||
|
BranchName = branchName; |
||||
|
RootPath = rootPath; |
||||
|
DependingRepositories = new List<GitRepository>(); |
||||
|
IgnoredDirectories = new List<string>(); |
||||
|
} |
||||
|
|
||||
|
public string GetUniqueName(string prefix) |
||||
|
{ |
||||
|
var name = Name + "_" + BranchName; |
||||
|
foreach (var dependingRepository in DependingRepositories) |
||||
|
{ |
||||
|
AddToUniqueName(dependingRepository, name); |
||||
|
} |
||||
|
|
||||
|
return (prefix.IsNullOrEmpty() ? "" : prefix + "_") + 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,143 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class GitRepositoryBuildStatus |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Name of the repository
|
||||
|
/// </summary>
|
||||
|
public string RepositoryName { 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 repositoryName, string branchName) |
||||
|
{ |
||||
|
RepositoryName = repositoryName; |
||||
|
BranchName = branchName; |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus>(); |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>(); |
||||
|
} |
||||
|
|
||||
|
public GitRepositoryBuildStatus GetSelfOrChild(string repositoryName) |
||||
|
{ |
||||
|
if (RepositoryName == repositoryName) |
||||
|
{ |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
return GetChild(repositoryName); |
||||
|
} |
||||
|
|
||||
|
public GitRepositoryBuildStatus GetChild(string repositoryName) |
||||
|
{ |
||||
|
foreach (var dependingRepository in DependingRepositories) |
||||
|
{ |
||||
|
var child = GetChildInternal(dependingRepository, repositoryName); |
||||
|
if (child != null) |
||||
|
{ |
||||
|
return child; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public string GetUniqueName(string prefix) |
||||
|
{ |
||||
|
var name = RepositoryName + "_" + BranchName; |
||||
|
foreach (var dependingRepository in DependingRepositories) |
||||
|
{ |
||||
|
AddToUniqueName(dependingRepository, name); |
||||
|
} |
||||
|
|
||||
|
return (prefix.IsNullOrEmpty() ? "" : prefix + "_") + name.ToMd5(); |
||||
|
} |
||||
|
|
||||
|
public void AddOrUpdateProjectStatus(DotNetProjectBuildStatus status) |
||||
|
{ |
||||
|
var existingProjectStatus = SucceedProjects.FirstOrDefault(p => p.CsProjPath == status.CsProjPath); |
||||
|
if (existingProjectStatus != null) |
||||
|
{ |
||||
|
existingProjectStatus.CommitId = status.CommitId; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
SucceedProjects.Add(status); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void MergeWith(GitRepositoryBuildStatus newBuildStatus) |
||||
|
{ |
||||
|
if (!newBuildStatus.CommitId.IsNullOrEmpty()) |
||||
|
{ |
||||
|
CommitId = newBuildStatus.CommitId; |
||||
|
} |
||||
|
|
||||
|
foreach (var succeedProject in newBuildStatus.SucceedProjects) |
||||
|
{ |
||||
|
AddOrUpdateProjectStatus(succeedProject); |
||||
|
} |
||||
|
|
||||
|
foreach (var dependingRepositoryBuildStatus in newBuildStatus.DependingRepositories) |
||||
|
{ |
||||
|
var existingDependingRepositoryBuildStatus = GetChild(dependingRepositoryBuildStatus.RepositoryName); |
||||
|
var newDependingRepositoryBuildStatus = newBuildStatus.GetChild( |
||||
|
dependingRepositoryBuildStatus.RepositoryName |
||||
|
); |
||||
|
|
||||
|
existingDependingRepositoryBuildStatus.MergeWith(newDependingRepositoryBuildStatus); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private GitRepositoryBuildStatus GetChildInternal(GitRepositoryBuildStatus repositoryBuildStatus, |
||||
|
string repositoryName) |
||||
|
{ |
||||
|
if (repositoryBuildStatus.RepositoryName == repositoryName) |
||||
|
{ |
||||
|
return repositoryBuildStatus; |
||||
|
} |
||||
|
|
||||
|
foreach (var dependingRepository in repositoryBuildStatus.DependingRepositories) |
||||
|
{ |
||||
|
var child = GetChildInternal(dependingRepository, repositoryName); |
||||
|
if (child != null) |
||||
|
{ |
||||
|
return child; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private void AddToUniqueName(GitRepositoryBuildStatus gitRepository, string name) |
||||
|
{ |
||||
|
name += "_" + gitRepository.RepositoryName + "_" + gitRepository.BranchName; |
||||
|
|
||||
|
foreach (var dependingRepository in gitRepository.DependingRepositories) |
||||
|
{ |
||||
|
AddToUniqueName(dependingRepository, name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using LibGit2Sharp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class GitRepositoryHelper : IGitRepositoryHelper, ITransientDependency |
||||
|
{ |
||||
|
public string GetLastCommitId(GitRepository repository) |
||||
|
{ |
||||
|
using (var repo = new Repository(string.Concat(repository.RootPath, @"\.git"))) |
||||
|
{ |
||||
|
return repo.Head.Tip.Id.ToString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string GetFriendlyName(GitRepository repository) |
||||
|
{ |
||||
|
using (var repo = new Repository(string.Concat(repository.RootPath, @"\.git"))) |
||||
|
{ |
||||
|
return repo.Head.FriendlyName; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public interface IBuildProjectListSorter |
||||
|
{ |
||||
|
List<DotNetProjectInfo> SortByDependencies( |
||||
|
List<DotNetProjectInfo> source, |
||||
|
IEqualityComparer<DotNetProjectInfo> comparer = null); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public interface IBuildStatusGenerator |
||||
|
{ |
||||
|
GitRepositoryBuildStatus Generate( |
||||
|
DotNetProjectBuildConfig buildConfig, |
||||
|
List<DotNetProjectInfo> changedProjects, |
||||
|
List<string> buildSucceededProjects |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public interface IChangedProjectFinder |
||||
|
{ |
||||
|
List<DotNetProjectInfo> Find(DotNetProjectBuildConfig buildConfig); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public interface IDotNetProjectBuildConfigReader |
||||
|
{ |
||||
|
DotNetProjectBuildConfig Read(string directoryPath); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public interface IDotNetProjectBuilder |
||||
|
{ |
||||
|
List<string> Build(List<DotNetProjectInfo> projects, 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,9 @@ |
|||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public interface IGitRepositoryHelper |
||||
|
{ |
||||
|
string GetLastCommitId(GitRepository repository); |
||||
|
|
||||
|
string GetFriendlyName(GitRepository repository); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public interface IRepositoryBuildStatusStore |
||||
|
{ |
||||
|
GitRepositoryBuildStatus Get(string buildNamePrefix, GitRepository repository); |
||||
|
|
||||
|
void Set(string buildNamePrefix, GitRepository repository, GitRepositoryBuildStatus status); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,132 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
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 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 IBuildStatusGenerator BuildStatusGenerator { get; set; } |
||||
|
|
||||
|
public IBuildProjectListSorter BuildProjectListSorter { get; set; } |
||||
|
|
||||
|
public Task ExecuteAsync(CommandLineArgs commandLineArgs) |
||||
|
{ |
||||
|
var sw = new Stopwatch(); |
||||
|
sw.Start(); |
||||
|
|
||||
|
var workingDirectory = commandLineArgs.Options.GetOrNull( |
||||
|
Options.WorkingDirectory.Short, |
||||
|
Options.WorkingDirectory.Long |
||||
|
); |
||||
|
|
||||
|
var dotnetBuildArguments = commandLineArgs.Options.GetOrNull( |
||||
|
Options.DotnetBuildArguments.Short, |
||||
|
Options.DotnetBuildArguments.Long |
||||
|
); |
||||
|
|
||||
|
var buildName = commandLineArgs.Options.GetOrNull( |
||||
|
Options.BuildName.Short, |
||||
|
Options.BuildName.Long |
||||
|
); |
||||
|
|
||||
|
var forceBuild = commandLineArgs.Options.ContainsKey(Options.ForceBuild.Short) || |
||||
|
commandLineArgs.Options.ContainsKey(Options.ForceBuild.Long); |
||||
|
|
||||
|
var buildConfig = DotNetProjectBuildConfigReader.Read(workingDirectory ?? Directory.GetCurrentDirectory()); |
||||
|
buildConfig.BuildName = buildName; |
||||
|
buildConfig.ForceBuild = forceBuild; |
||||
|
|
||||
|
Console.WriteLine("Finding changed projects..."); |
||||
|
|
||||
|
var changedProjectFiles = ChangedProjectFinder.Find(buildConfig); |
||||
|
|
||||
|
var buildSucceededProjects = DotNetProjectBuilder.Build( |
||||
|
changedProjectFiles, |
||||
|
dotnetBuildArguments ?? "" |
||||
|
); |
||||
|
|
||||
|
var buildStatus = BuildStatusGenerator.Generate( |
||||
|
buildConfig, |
||||
|
changedProjectFiles, |
||||
|
buildSucceededProjects |
||||
|
); |
||||
|
|
||||
|
RepositoryBuildStatusStore.Set(buildName, buildConfig.GitRepository, buildStatus); |
||||
|
|
||||
|
sw.Stop(); |
||||
|
Console.WriteLine("Build operation is completed in " + sw.ElapsedMilliseconds + " (ms)"); |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
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(""); |
||||
|
sb.AppendLine("-wd|--working-directory <directory-path> (default: empty)"); |
||||
|
sb.AppendLine("-m |--max-parallel-builds <parallel-build-count> (default: 1)"); |
||||
|
sb.AppendLine("-a |--dotnet-build-arguments <arguments> (default: empty)"); |
||||
|
sb.AppendLine("-n |--build-name <name> (default: empty)"); |
||||
|
sb.AppendLine("-f | --force (default: false)"); |
||||
|
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 WorkingDirectory |
||||
|
{ |
||||
|
public const string Short = "wd"; |
||||
|
public const string Long = "working-directory"; |
||||
|
} |
||||
|
|
||||
|
public static class DotnetBuildArguments |
||||
|
{ |
||||
|
public const string Short = "a"; |
||||
|
public const string Long = "dotnet-build-arguments"; |
||||
|
} |
||||
|
|
||||
|
public static class BuildName |
||||
|
{ |
||||
|
public const string Short = "n"; |
||||
|
public const string Long = "build-name"; |
||||
|
} |
||||
|
|
||||
|
public static class ForceBuild |
||||
|
{ |
||||
|
public const string Short = "f"; |
||||
|
public const string Long = "force"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class BuildProjectListSorter_Tests : AbpCliTestBase |
||||
|
{ |
||||
|
private IBuildProjectListSorter _buildProjectListSorter; |
||||
|
|
||||
|
public BuildProjectListSorter_Tests() |
||||
|
{ |
||||
|
_buildProjectListSorter = GetRequiredService<IBuildProjectListSorter>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void SortByDependencies_Test() |
||||
|
{ |
||||
|
// A -> B, C
|
||||
|
// B -> D
|
||||
|
// D -> F
|
||||
|
// F -> C
|
||||
|
// C -> G
|
||||
|
// Final build order must be: G,
|
||||
|
|
||||
|
var repositoryName = "volo"; |
||||
|
var source = new List<DotNetProjectInfo> |
||||
|
{ |
||||
|
new DotNetProjectInfo(repositoryName, "A", true) |
||||
|
{ |
||||
|
Dependencies = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo(repositoryName, "B", true), |
||||
|
new DotNetProjectInfo(repositoryName, "C", true) |
||||
|
} |
||||
|
}, |
||||
|
new DotNetProjectInfo(repositoryName, "B", true) |
||||
|
{ |
||||
|
Dependencies = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo(repositoryName, "D", true) |
||||
|
} |
||||
|
}, |
||||
|
new DotNetProjectInfo(repositoryName, "D", true) |
||||
|
{ |
||||
|
Dependencies = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo(repositoryName, "F", true) |
||||
|
} |
||||
|
}, |
||||
|
new DotNetProjectInfo(repositoryName, "F", true) |
||||
|
{ |
||||
|
Dependencies = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo(repositoryName, "C", true) |
||||
|
} |
||||
|
}, |
||||
|
new DotNetProjectInfo(repositoryName, "C", true) |
||||
|
{ |
||||
|
Dependencies = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo(repositoryName, "G", true) |
||||
|
} |
||||
|
}, |
||||
|
new DotNetProjectInfo(repositoryName, "G", true) |
||||
|
}; |
||||
|
|
||||
|
var sortedDependencies = |
||||
|
_buildProjectListSorter.SortByDependencies(source, new DotNetProjectInfoEqualityComparer()); |
||||
|
sortedDependencies.Count.ShouldBe(6); |
||||
|
sortedDependencies[0].CsProjPath.ShouldBe("G"); |
||||
|
sortedDependencies[1].CsProjPath.ShouldBe("C"); |
||||
|
sortedDependencies[2].CsProjPath.ShouldBe("F"); |
||||
|
sortedDependencies[3].CsProjPath.ShouldBe("D"); |
||||
|
sortedDependencies[4].CsProjPath.ShouldBe("B"); |
||||
|
sortedDependencies[5].CsProjPath.ShouldBe("A"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,123 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using NSubstitute; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class BuildStatusGenerator_Tests : AbpCliTestBase |
||||
|
{ |
||||
|
private readonly IBuildStatusGenerator _buildStatusGenerator; |
||||
|
private IGitRepositoryHelper _gitRepositoryHelper; |
||||
|
|
||||
|
public BuildStatusGenerator_Tests() |
||||
|
{ |
||||
|
_buildStatusGenerator = GetRequiredService<IBuildStatusGenerator>(); |
||||
|
} |
||||
|
|
||||
|
protected override void AfterAddApplication(IServiceCollection services) |
||||
|
{ |
||||
|
_gitRepositoryHelper = Substitute.For<IGitRepositoryHelper>(); |
||||
|
services.AddTransient(provider => _gitRepositoryHelper); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Set_Repository_CommitId_When_All_Projects_Built() |
||||
|
{ |
||||
|
var buildConfig = new DotNetProjectBuildConfig |
||||
|
{ |
||||
|
GitRepository = new GitRepository("volo", "dev", "") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepository>() |
||||
|
{ |
||||
|
new GitRepository("abp", "dev", "") |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var changedProjects = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo("volo", "project1.csproj", true) |
||||
|
}; |
||||
|
|
||||
|
var builtProjects = new List<string>() |
||||
|
{ |
||||
|
"project1.csproj" |
||||
|
}; |
||||
|
|
||||
|
var lastCommitId = "1"; |
||||
|
_gitRepositoryHelper.GetLastCommitId(buildConfig.GitRepository).Returns(lastCommitId); |
||||
|
_gitRepositoryHelper.GetFriendlyName(buildConfig.GitRepository).Returns("volo"); |
||||
|
|
||||
|
var status = _buildStatusGenerator.Generate(buildConfig, changedProjects, builtProjects); |
||||
|
status.CommitId.ShouldBe(lastCommitId); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Set_Repository_CommitId_When_All_Projects_Built_For_Child_Repository() |
||||
|
{ |
||||
|
var buildConfig = new DotNetProjectBuildConfig |
||||
|
{ |
||||
|
GitRepository = new GitRepository("volo", "dev", "") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepository>() |
||||
|
{ |
||||
|
new GitRepository("abp", "dev", "") |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var changedProjects = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo("abp", "project1.csproj", true) |
||||
|
}; |
||||
|
|
||||
|
var builtProjects = new List<string>() |
||||
|
{ |
||||
|
"project1.csproj" |
||||
|
}; |
||||
|
|
||||
|
var lastCommitId = "1"; |
||||
|
_gitRepositoryHelper.GetLastCommitId(buildConfig.GitRepository).Returns(lastCommitId); |
||||
|
_gitRepositoryHelper.GetFriendlyName(buildConfig.GitRepository).Returns("abp"); |
||||
|
|
||||
|
var status = _buildStatusGenerator.Generate(buildConfig, changedProjects, builtProjects); |
||||
|
status.CommitId.ShouldBe(lastCommitId); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Set_Repository_CommitId_When_Building_Single_Solution() |
||||
|
{ |
||||
|
var buildConfig = new DotNetProjectBuildConfig |
||||
|
{ |
||||
|
GitRepository = new GitRepository("volo", "dev", "") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepository>() |
||||
|
{ |
||||
|
new GitRepository("abp", "dev", "") |
||||
|
} |
||||
|
}, |
||||
|
SlFilePath = "test.sln" |
||||
|
}; |
||||
|
|
||||
|
var changedProjects = new List<DotNetProjectInfo>() |
||||
|
{ |
||||
|
new DotNetProjectInfo("volo", "project1.csproj", true) |
||||
|
}; |
||||
|
|
||||
|
var builtProjects = new List<string>() |
||||
|
{ |
||||
|
"project1.csproj" |
||||
|
}; |
||||
|
|
||||
|
var lastCommitId = "1"; |
||||
|
_gitRepositoryHelper.GetLastCommitId(buildConfig.GitRepository).Returns(lastCommitId); |
||||
|
_gitRepositoryHelper.GetFriendlyName(buildConfig.GitRepository).Returns("volo"); |
||||
|
|
||||
|
var status = _buildStatusGenerator.Generate(buildConfig, changedProjects, builtProjects); |
||||
|
status.CommitId.ShouldBeNull(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,318 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using NSubstitute; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class GitRepositoryBuildStatus_Tests : AbpCliTestBase |
||||
|
{ |
||||
|
private IGitRepositoryHelper _gitRepositoryHelper; |
||||
|
|
||||
|
protected override void AfterAddApplication(IServiceCollection services) |
||||
|
{ |
||||
|
_gitRepositoryHelper = Substitute.For<IGitRepositoryHelper>(); |
||||
|
services.AddTransient(provider => _gitRepositoryHelper); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Add_New_Build_Status_Test() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("volo", "dev") |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "project1.csproj", |
||||
|
CommitId = "1" |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var newBuildStatus = new GitRepositoryBuildStatus( |
||||
|
existingBuildStatus.RepositoryName, |
||||
|
existingBuildStatus.BranchName |
||||
|
) |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "project2.csproj", |
||||
|
CommitId = "2" |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.MergeWith(newBuildStatus); |
||||
|
|
||||
|
existingBuildStatus.SucceedProjects.Count.ShouldBe(2); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Update_Existing_Build_Status_Test() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("volo", "dev") |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "project1.csproj", |
||||
|
CommitId = "1" |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var newBuildStatus = new GitRepositoryBuildStatus( |
||||
|
existingBuildStatus.RepositoryName, |
||||
|
existingBuildStatus.BranchName |
||||
|
) |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "project1.csproj", |
||||
|
CommitId = "2" |
||||
|
}, |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "project2.csproj", |
||||
|
CommitId = "2" |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.MergeWith(newBuildStatus); |
||||
|
existingBuildStatus.SucceedProjects.Count.ShouldBe(2); |
||||
|
existingBuildStatus.GetSelfOrChild("volo").SucceedProjects.First(p => p.CsProjPath == "project1.csproj") |
||||
|
.CommitId.ShouldBe("2"); |
||||
|
existingBuildStatus.GetSelfOrChild("volo").SucceedProjects.First(p => p.CsProjPath == "project2.csproj") |
||||
|
.CommitId.ShouldBe("2"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Add_New_Build_Status_For_Child_Repository_Test() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("volo", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("abp", "dev") |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "project1.csproj", |
||||
|
CommitId = "1" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var newBuildStatus = new GitRepositoryBuildStatus( |
||||
|
existingBuildStatus.RepositoryName, |
||||
|
existingBuildStatus.BranchName |
||||
|
) |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("abp", "dev") |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "project2.csproj", |
||||
|
CommitId = "2" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.MergeWith(newBuildStatus); |
||||
|
existingBuildStatus.GetChild("abp").SucceedProjects.Count.ShouldBe(2); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Update_Repository_CommitId_When_New_CommitId_Is_Not_Empty() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("volo", "dev"); |
||||
|
|
||||
|
var newBuildStatus = new GitRepositoryBuildStatus( |
||||
|
existingBuildStatus.RepositoryName, |
||||
|
existingBuildStatus.BranchName |
||||
|
) |
||||
|
{ |
||||
|
CommitId = "42" |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.MergeWith(newBuildStatus); |
||||
|
|
||||
|
existingBuildStatus.CommitId.ShouldBe("42"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Update_Repository_CommitId_When_New_CommitId_Is_Empty() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("volo", "dev") |
||||
|
{ |
||||
|
CommitId = "21" |
||||
|
}; |
||||
|
|
||||
|
var newBuildStatus = new GitRepositoryBuildStatus( |
||||
|
existingBuildStatus.RepositoryName, |
||||
|
existingBuildStatus.BranchName |
||||
|
) |
||||
|
{ |
||||
|
CommitId = "" |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.MergeWith(newBuildStatus); |
||||
|
|
||||
|
existingBuildStatus.CommitId.ShouldBe("21"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetChild_Test() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("repo-1", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-2", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-3", "dev") |
||||
|
} |
||||
|
}, |
||||
|
new GitRepositoryBuildStatus("repo-4", "dev") |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.GetChild("repo-3").RepositoryName.ShouldBe("repo-3"); |
||||
|
existingBuildStatus.GetChild("repo-4").RepositoryName.ShouldBe("repo-4"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetUniqueName_Test() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("repo-1", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-2", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-3", "dev") |
||||
|
} |
||||
|
}, |
||||
|
new GitRepositoryBuildStatus("repo-4", "dev") |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.GetUniqueName("").ShouldBe("B25C935F97D7B3375530A96B392B7644"); |
||||
|
existingBuildStatus.GetUniqueName("production").ShouldBe("production_B25C935F97D7B3375530A96B392B7644"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void GetSelfOrChild_Test() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("repo-1", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-2", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-3", "dev") |
||||
|
} |
||||
|
}, |
||||
|
new GitRepositoryBuildStatus("repo-4", "dev") |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
existingBuildStatus.GetSelfOrChild("repo-1").RepositoryName.ShouldBe("repo-1"); |
||||
|
existingBuildStatus.GetSelfOrChild("repo-2").RepositoryName.ShouldBe("repo-2"); |
||||
|
existingBuildStatus.GetSelfOrChild("repo-3").RepositoryName.ShouldBe("repo-3"); |
||||
|
existingBuildStatus.GetSelfOrChild("repo-4").RepositoryName.ShouldBe("repo-4"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddOrUpdateProjectStatus_Test() |
||||
|
{ |
||||
|
var existingBuildStatus = new GitRepositoryBuildStatus("repo-1", "dev") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus>() |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-2", "dev") |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "A.csproj", |
||||
|
CommitId = "42" |
||||
|
} |
||||
|
}, |
||||
|
DependingRepositories = new List<GitRepositoryBuildStatus> |
||||
|
{ |
||||
|
new GitRepositoryBuildStatus("repo-3", "dev") |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus> |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "B.csproj", |
||||
|
CommitId = "42" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
new GitRepositoryBuildStatus("repo-4", "dev") |
||||
|
{ |
||||
|
SucceedProjects = new List<DotNetProjectBuildStatus>() |
||||
|
{ |
||||
|
new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CsProjPath = "C.csproj", |
||||
|
CommitId = "42" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var repo2 = existingBuildStatus.GetChild("repo-2"); |
||||
|
repo2.AddOrUpdateProjectStatus(new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CommitId = "21", |
||||
|
CsProjPath = "A.csproj" |
||||
|
}); |
||||
|
|
||||
|
var repo3 = existingBuildStatus.GetChild("repo-3"); |
||||
|
repo3.AddOrUpdateProjectStatus(new DotNetProjectBuildStatus |
||||
|
{ |
||||
|
CommitId = "21", |
||||
|
CsProjPath = "X.csproj" |
||||
|
}); |
||||
|
|
||||
|
repo2.SucceedProjects.Count.ShouldBe(1); |
||||
|
repo2.SucceedProjects.ShouldContain(e=> e.CsProjPath == "A.csproj" && e.CommitId == "21"); |
||||
|
|
||||
|
repo3.SucceedProjects.Count.ShouldBe(2); |
||||
|
repo3.SucceedProjects.ShouldContain(e=> e.CsProjPath == "X.csproj" && e.CommitId == "21"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Cli.Build |
||||
|
{ |
||||
|
public class GitRepository_Tests : AbpCliTestBase |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void GetUniqueName_Test() |
||||
|
{ |
||||
|
var gitRepository = new GitRepository("repo-1", "dev", "") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepository> |
||||
|
{ |
||||
|
new GitRepository("repo-2", "dev", ""), |
||||
|
new GitRepository("repo-3", "dev", "") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepository>() |
||||
|
{ |
||||
|
new GitRepository("repo-4", "dev", "") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
gitRepository.GetUniqueName("").ShouldBe("B25C935F97D7B3375530A96B392B7644"); |
||||
|
gitRepository.GetUniqueName("production").ShouldBe("production_B25C935F97D7B3375530A96B392B7644"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void FindRepositoryOf_Test() |
||||
|
{ |
||||
|
var gitRepository = new GitRepository("repo-1", "dev", "/repo-1/dev/") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepository> |
||||
|
{ |
||||
|
new GitRepository("repo-2", "dev", "/repo-2/dev/"), |
||||
|
new GitRepository("repo-3", "dev", "/repo-3/dev/") |
||||
|
{ |
||||
|
DependingRepositories = new List<GitRepository>() |
||||
|
{ |
||||
|
new GitRepository("repo-4", "dev", "/repo-4/dev/") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
gitRepository.FindRepositoryOf("/repo-4/dev/A.csproj").ShouldBe("repo-4"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue