From 9256cb47aad626f252d5c51e6c9351b41fae2ca9 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Fri, 22 Sep 2023 15:25:33 +0200 Subject: [PATCH 1/3] Support sub modules in mocks --- NGitLab.Mock.Tests/ProjectsMockTests.cs | 21 +++++++ NGitLab.Mock/Config/GitLabCommit.cs | 5 ++ NGitLab.Mock/Config/GitLabHelpers.cs | 58 +++++++++++++++++-- .../Config/GitLabSubModuleDescriptor.cs | 13 +++++ NGitLab.Mock/PublicAPI.Unshipped.txt | 7 +++ NGitLab.Mock/Repository.cs | 12 +++- 6 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 NGitLab.Mock/Config/GitLabSubModuleDescriptor.cs diff --git a/NGitLab.Mock.Tests/ProjectsMockTests.cs b/NGitLab.Mock.Tests/ProjectsMockTests.cs index ff865f98..706f3f67 100644 --- a/NGitLab.Mock.Tests/ProjectsMockTests.cs +++ b/NGitLab.Mock.Tests/ProjectsMockTests.cs @@ -37,6 +37,27 @@ public void Test_project_can_be_cloned_by_default() Assert.IsTrue(Directory.Exists(tempDir.GetFullPath(".git"))); } + [Test] + public void Test_project_with_submodules() + { + using var tempDir = TemporaryDirectory.Create(); + using var server = new GitLabConfig() + .WithUser("Test", isDefault: true) + .WithProject("ModuleA", configure: x => x.WithCommit(configure: c => c.WithFile("A.txt"))) + .WithProject("ModuleB", configure: x => x.WithCommit(configure: c => c.WithFile("B.txt"))) + .WithProject("Test", clonePath: tempDir.FullPath, configure: x => + x.WithCommit("Init", configure: c + => c.WithSubModule("ModuleA") + .WithSubModule("ModuleB"))) + .BuildServer(); + + Assert.IsTrue(Directory.Exists(tempDir.GetFullPath(".git"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleA/.git"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleA/A.txt"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleB/.git"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleB/B.txt"))); + } + [Test] public void Test_projects_created_url_ends_with_namespace_and_name() { diff --git a/NGitLab.Mock/Config/GitLabCommit.cs b/NGitLab.Mock/Config/GitLabCommit.cs index 585e879e..7034142e 100644 --- a/NGitLab.Mock/Config/GitLabCommit.cs +++ b/NGitLab.Mock/Config/GitLabCommit.cs @@ -24,6 +24,11 @@ public class GitLabCommit : GitLabObject /// public IList Files { get; } = new List(); + /// + /// Submodules added at this commit + /// + public IList SubModules { get; } = new List(); + public IList Tags { get; } = new List(); /// diff --git a/NGitLab.Mock/Config/GitLabHelpers.cs b/NGitLab.Mock/Config/GitLabHelpers.cs index e5d7eb51..72f31aff 100644 --- a/NGitLab.Mock/Config/GitLabHelpers.cs +++ b/NGitLab.Mock/Config/GitLabHelpers.cs @@ -448,6 +448,20 @@ public static GitLabCommit WithFile(this GitLabCommit commit, string relativePat }); } + /// + /// Add a submodule in commit + /// + public static GitLabCommit WithSubModule(this GitLabCommit commit, string projectName) + { + return Configure(commit, _ => + { + commit.SubModules.Add(new GitLabSubModuleDescriptor + { + ProjectName = projectName, + }); + }); + } + /// /// Add an issue description in project /// @@ -1173,7 +1187,7 @@ private static void CreateProject(GitLabServer server, GitLabProject project) StartInfo = new ProcessStartInfo { FileName = "git", - Arguments = $"clone {project.CloneParameters} \"{prj.SshUrl}\" \"{Path.GetFileName(project.ClonePath)}\"", + Arguments = $"-c protocol.file.allow=always clone {project.CloneParameters} \"{prj.SshUrl}\" \"{Path.GetFileName(project.ClonePath)}\" --recursive", RedirectStandardError = true, UseShellExecute = false, WorkingDirectory = folderPath, @@ -1222,9 +1236,12 @@ private static Commit CreateCommit(GitLabServer server, Project prj, GitLabCommi prj.Repository.CreateBranch(commit.SourceBranch); var files = commit.Files.Count == 0 - ? new[] { File.CreateFromText("test.txt", Guid.NewGuid().ToString()) } - : commit.Files.Select(x => File.CreateFromText(x.Path, x.Content ?? string.Empty)); - cmt = prj.Repository.Commit(user, commit.Message ?? Guid.NewGuid().ToString("D"), commit.SourceBranch, files); + ? new List() { File.CreateFromText("test.txt", Guid.NewGuid().ToString()) } + : commit.Files.Select(x => File.CreateFromText(x.Path, x.Content ?? string.Empty)).ToList(); + + var submodules = CreateSubModules(server, prj, commit); + + cmt = prj.Repository.Commit(user, commit.Message ?? Guid.NewGuid().ToString("D"), commit.SourceBranch, files, submodules); } else { @@ -1241,6 +1258,39 @@ private static Commit CreateCommit(GitLabServer server, Project prj, GitLabCommi return cmt; } + private static IEnumerable CreateSubModules(GitLabServer server, Project prj, GitLabCommit commit) + { + List submodules = new(); + foreach (var submodule in commit.SubModules) + { + var subModuleProject = server.AllProjects.FirstOrDefault(x => + x.Name.Equals(submodule.ProjectName, StringComparison.OrdinalIgnoreCase)); + if (subModuleProject is null) + { + throw new GitLabException($"Project {submodule.ProjectName} can't be found."); + } + + if (!subModuleProject.Repository.GetCommits().Any()) + { + throw new GitLabException("Project added as a module must have least one commit."); + } + + using var process = Process.Start( + new ProcessStartInfo("git", $"-c protocol.file.allow=always submodule add {subModuleProject.SshUrl} {subModuleProject.Name}") + { + WorkingDirectory = prj.Repository.FullPath, + }); + + process.WaitForExit(); + if (process.ExitCode != 0) + throw new GitLabException("Cannot add submodule"); + + submodules.Add(subModuleProject.Name); + } + + return submodules; + } + private static void CreateLabel(Group group, GitLabLabel label) { group.Labels.Add(new Label diff --git a/NGitLab.Mock/Config/GitLabSubModuleDescriptor.cs b/NGitLab.Mock/Config/GitLabSubModuleDescriptor.cs new file mode 100644 index 00000000..588a0984 --- /dev/null +++ b/NGitLab.Mock/Config/GitLabSubModuleDescriptor.cs @@ -0,0 +1,13 @@ +namespace NGitLab.Mock.Config +{ + /// + /// Describe a sub module in project repository + /// + public class GitLabSubModuleDescriptor + { + /// + /// Project's ID added as a submodule + /// + public string ProjectName { get; init; } + } +} diff --git a/NGitLab.Mock/PublicAPI.Unshipped.txt b/NGitLab.Mock/PublicAPI.Unshipped.txt index 0410bf1e..02648e5f 100644 --- a/NGitLab.Mock/PublicAPI.Unshipped.txt +++ b/NGitLab.Mock/PublicAPI.Unshipped.txt @@ -124,6 +124,7 @@ NGitLab.Mock.Config.GitLabCommit.DeleteSourceBranch.get -> bool NGitLab.Mock.Config.GitLabCommit.DeleteSourceBranch.set -> void NGitLab.Mock.Config.GitLabCommit.FromBranch.get -> string NGitLab.Mock.Config.GitLabCommit.FromBranch.set -> void +NGitLab.Mock.Config.GitLabCommit.SubModules.get -> System.Collections.Generic.IList NGitLab.Mock.Config.GitLabConfig.DefaultBranch.get -> string NGitLab.Mock.Config.GitLabConfig.DefaultBranch.set -> void NGitLab.Mock.Config.GitLabConfig.DefaultUser.get -> string @@ -207,6 +208,10 @@ NGitLab.Mock.Config.GitLabReleaseInfo.ReleasedAt.set -> void NGitLab.Mock.Config.GitLabReleaseInfo.TagName.get -> string NGitLab.Mock.Config.GitLabReleaseInfo.TagName.set -> void NGitLab.Mock.Config.GitLabReleaseInfoCollection +NGitLab.Mock.Config.GitLabSubModuleDescriptor +NGitLab.Mock.Config.GitLabSubModuleDescriptor.GitLabSubModuleDescriptor() -> void +NGitLab.Mock.Config.GitLabSubModuleDescriptor.ProjectName.get -> string +NGitLab.Mock.Config.GitLabSubModuleDescriptor.ProjectName.init -> void NGitLab.Mock.EffectivePermissions NGitLab.Mock.EffectivePermissions.GetAccessLevel(NGitLab.Mock.User user) -> NGitLab.Models.AccessLevel? NGitLab.Mock.EffectivePermissions.GetEffectivePermission(NGitLab.Mock.User user) -> NGitLab.Mock.EffectiveUserPermission @@ -945,6 +950,7 @@ NGitLab.Mock.Repository.Checkout(string committishOrBranchNameSpec) -> void NGitLab.Mock.Repository.CherryPick(NGitLab.Models.CommitCherryPick commitCherryPick) -> LibGit2Sharp.Commit NGitLab.Mock.Repository.Commit(NGitLab.Mock.User user, string message) -> LibGit2Sharp.Commit NGitLab.Mock.Repository.Commit(NGitLab.Mock.User user, string message, string targetBranch, System.Collections.Generic.IEnumerable files) -> LibGit2Sharp.Commit +NGitLab.Mock.Repository.Commit(NGitLab.Mock.User user, string message, string targetBranch, System.Collections.Generic.IEnumerable files, System.Collections.Generic.IEnumerable submodules) -> LibGit2Sharp.Commit NGitLab.Mock.Repository.Commit(NGitLab.Mock.User user, string message, System.Collections.Generic.IEnumerable files) -> LibGit2Sharp.Commit NGitLab.Mock.Repository.Commit(NGitLab.Models.CommitCreate commitCreate) -> LibGit2Sharp.Commit NGitLab.Mock.Repository.CreateAndCheckoutBranch(string branchName) -> LibGit2Sharp.Branch @@ -1216,6 +1222,7 @@ static NGitLab.Mock.Config.GitLabHelpers.WithMilestone(this NGitLab.Mock.Config. static NGitLab.Mock.Config.GitLabHelpers.WithPipeline(this NGitLab.Mock.Config.GitLabProject project, string ref, System.Action configure) -> NGitLab.Mock.Config.GitLabProject static NGitLab.Mock.Config.GitLabHelpers.WithProject(this NGitLab.Mock.Config.GitLabConfig config, string name = null, int id = 0, string namespace = null, string description = null, string defaultBranch = null, NGitLab.Models.VisibilityLevel visibility = NGitLab.Models.VisibilityLevel.Internal, bool initialCommit = false, bool addDefaultUserAsMaintainer = false, string clonePath = null, string cloneParameters = null, System.Action configure = null) -> NGitLab.Mock.Config.GitLabConfig static NGitLab.Mock.Config.GitLabHelpers.WithRelease(this NGitLab.Mock.Config.GitLabProject project, string author, string tagName, System.DateTime? createdAt = null, System.DateTime? releasedAt = null) -> NGitLab.Mock.Config.GitLabProject +static NGitLab.Mock.Config.GitLabHelpers.WithSubModule(this NGitLab.Mock.Config.GitLabCommit commit, string projectName) -> NGitLab.Mock.Config.GitLabCommit static NGitLab.Mock.Config.GitLabHelpers.WithSystemComment(this NGitLab.Mock.Config.GitLabIssue issue, string message = null, string innerHtml = null, int id = 0, string author = null, System.DateTime? createdAt = null, System.DateTime? updatedAt = null) -> NGitLab.Mock.Config.GitLabIssue static NGitLab.Mock.Config.GitLabHelpers.WithSystemComment(this NGitLab.Mock.Config.GitLabMergeRequest mergeRequest, string message = null, string innerHtml = null, int id = 0, string author = null, System.DateTime? createdAt = null, System.DateTime? updatedAt = null) -> NGitLab.Mock.Config.GitLabMergeRequest static NGitLab.Mock.Config.GitLabHelpers.WithUser(this NGitLab.Mock.Config.GitLabConfig config, string username, string name = null, string email = null, string avatarUrl = null, bool isAdmin = false, bool isDefault = false, System.Action configure = null) -> NGitLab.Mock.Config.GitLabConfig diff --git a/NGitLab.Mock/Repository.cs b/NGitLab.Mock/Repository.cs index fb7183f9..11b07f48 100644 --- a/NGitLab.Mock/Repository.cs +++ b/NGitLab.Mock/Repository.cs @@ -157,7 +157,7 @@ public IReadOnlyCollection GetAllBranches() public Commit Commit(User user, string message) { - return Commit(user, message, targetBranch: null, new[] { File.CreateFromText("test.txt", Guid.NewGuid().ToString()) }); + return Commit(user, message, targetBranch: null, new[] { File.CreateFromText("test.txt", Guid.NewGuid().ToString()) }, Enumerable.Empty()); } public Commit Commit(User user, string message, IEnumerable files) @@ -166,6 +166,11 @@ public Commit Commit(User user, string message, IEnumerable files) } public Commit Commit(User user, string message, string targetBranch, IEnumerable files) + { + return Commit(user, message, targetBranch, files, Enumerable.Empty()); + } + + public Commit Commit(User user, string message, string targetBranch, IEnumerable files, IEnumerable submodules) { var repository = GetGitRepository(); if (targetBranch != null) @@ -181,6 +186,11 @@ public Commit Commit(User user, string message, string targetBranch, IEnumerable repository.Index.Add(file.Path); } + foreach (var submodule in submodules) + { + repository.Index.Add(submodule); + } + repository.Index.Write(); var author = new Signature(user.UserName, user.Email, DateTimeOffset.UtcNow); From 737ecb214ce02d4c0736c80b2aa596aa4b99cc09 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Fri, 22 Sep 2023 15:52:52 +0200 Subject: [PATCH 2/3] Clean --- NGitLab.Mock.Tests/ProjectsMockTests.cs | 22 ++++++++++++++++++++++ NGitLab.Mock/Config/GitLabHelpers.cs | 7 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/NGitLab.Mock.Tests/ProjectsMockTests.cs b/NGitLab.Mock.Tests/ProjectsMockTests.cs index 706f3f67..062892d8 100644 --- a/NGitLab.Mock.Tests/ProjectsMockTests.cs +++ b/NGitLab.Mock.Tests/ProjectsMockTests.cs @@ -58,6 +58,28 @@ public void Test_project_with_submodules() Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleB/B.txt"))); } + [Test] + public void Test_project_with_nested_submodules() + { + using var tempDir = TemporaryDirectory.Create(); + using var server = new GitLabConfig() + .WithUser("Test", isDefault: true) + .WithProject("ModuleA", configure: x => x.WithCommit(configure: c => c.WithFile("A.txt"))) + .WithProject("ModuleB", configure: x => x.WithCommit(configure: c + => c.WithFile("B.txt") + .WithSubModule("ModuleA"))) + .WithProject("Test", clonePath: tempDir.FullPath, configure: x => + x.WithCommit(configure: c + => c.WithSubModule("ModuleB"))) + .BuildServer(); + + Assert.IsTrue(Directory.Exists(tempDir.GetFullPath(".git"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleB/.git"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleB/B.txt"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleB/ModuleA/.git"))); + Assert.IsTrue(System.IO.File.Exists(tempDir.GetFullPath("ModuleB/ModuleA/A.txt"))); + } + [Test] public void Test_projects_created_url_ends_with_namespace_and_name() { diff --git a/NGitLab.Mock/Config/GitLabHelpers.cs b/NGitLab.Mock/Config/GitLabHelpers.cs index 72f31aff..005fa3cf 100644 --- a/NGitLab.Mock/Config/GitLabHelpers.cs +++ b/NGitLab.Mock/Config/GitLabHelpers.cs @@ -1278,12 +1278,17 @@ private static IEnumerable CreateSubModules(GitLabServer server, Project using var process = Process.Start( new ProcessStartInfo("git", $"-c protocol.file.allow=always submodule add {subModuleProject.SshUrl} {subModuleProject.Name}") { + RedirectStandardError = true, + UseShellExecute = false, WorkingDirectory = prj.Repository.FullPath, }); process.WaitForExit(); if (process.ExitCode != 0) - throw new GitLabException("Cannot add submodule"); + { + var error = process.StandardError.ReadToEnd(); + throw new GitLabException($"Cannot add submodule: {error}"); + } submodules.Add(subModuleProject.Name); } From a11e42cf30a1dd16d98160e99149ca0903497fc5 Mon Sep 17 00:00:00 2001 From: Julien Richard Date: Thu, 28 Sep 2023 17:41:12 +0200 Subject: [PATCH 3/3] Update NGitLab.Mock/Config/GitLabHelpers.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gérald Barré --- NGitLab.Mock/Config/GitLabHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab.Mock/Config/GitLabHelpers.cs b/NGitLab.Mock/Config/GitLabHelpers.cs index 1f43107f..3ae2e4c8 100644 --- a/NGitLab.Mock/Config/GitLabHelpers.cs +++ b/NGitLab.Mock/Config/GitLabHelpers.cs @@ -1337,7 +1337,7 @@ private static IEnumerable CreateSubModules(GitLabServer server, Project } using var process = Process.Start( - new ProcessStartInfo("git", $"-c protocol.file.allow=always submodule add {subModuleProject.SshUrl} {subModuleProject.Name}") + new ProcessStartInfo("git", $"-c protocol.file.allow=always submodule add \"{subModuleProject.SshUrl}\" \"{subModuleProject.Name}\"") { RedirectStandardError = true, UseShellExecute = false,