internal async Task <IReadOnlyList <UpdateResult> > Sync( Mapper diff, SyncOutput expectedOutput, IEnumerable <string> labelsToApplyOnPullRequests = null, string description = null) { Guard.AgainstNull(diff, nameof(diff)); Guard.AgainstNull(expectedOutput, nameof(expectedOutput)); var labels = labelsToApplyOnPullRequests?.ToArray() ?? new string[] { }; if (labels.Any() && expectedOutput != SyncOutput.CreatePullRequest) { throw new Exception($"Labels can only be applied in '{SyncOutput.CreatePullRequest}' mode."); } var t = diff.Transpose(); var results = new List <UpdateResult>(); foreach (var updatesPerOwnerRepositoryBranch in t.Values) { var updates = await ProcessUpdates(expectedOutput, updatesPerOwnerRepositoryBranch, labels, description); results.Add(updates); } return(results); }
private void WriteSyncOutput(SyncOutput output) { if (output != null) { if (output.Error) { Host.UI.RawUI.ForegroundColor = ConsoleColor.Red; } WriteObject(output); if (output.Error) { Host.UI.RawUI.ForegroundColor = hostColor; } } }
internal async Task <bool> CanSynchronize(RepositoryInfo targetRepository, SyncOutput expectedOutput) { if (expectedOutput == SyncOutput.CreatePullRequest || expectedOutput == SyncOutput.MergePullRequest) { var hasOpenPullRequests = await gateway.HasOpenPullRequests(targetRepository.Owner, targetRepository.Repository, PullRequestTitle); if (hasOpenPullRequests) { log("Cannot create pull request, there is an existing open pull request, close or merge that first"); return(false); } } return(true); }
public async Task <IReadOnlyList <UpdateResult> > Sync(SyncOutput syncOutput = SyncOutput.CreatePullRequest) { var list = new List <UpdateResult>(); foreach (var targetRepository in targets) { var targetRepositoryDisplayName = $"{targetRepository.Owner}/{targetRepository.Repository}"; using var syncer = new Syncer(targetRepository.Credentials, null, log); if (!await syncer.CanSynchronize(targetRepository, syncOutput, targetRepository.Branch)) { continue; } var syncContext = await CalculateSyncContext(targetRepository); if (!syncContext.Diff.ToBeAddedOrUpdatedEntries.Any()) { log($"Repo {targetRepositoryDisplayName} is in sync"); continue; } var sync = await syncer.Sync(syncContext.Diff, syncOutput, labelsToApplyOnPullRequests, syncContext.Description); var createdSyncBranch = sync.FirstOrDefault(); if (createdSyncBranch == null) { log($"Repo {targetRepositoryDisplayName} is in sync"); } else { log($"Pull created for {targetRepositoryDisplayName}, click here to review and pull: {createdSyncBranch}"); } list.Add(createdSyncBranch); } return(list); }
public async Task Sync(SyncOutput syncOutput = SyncOutput.CreatePullRequest) { foreach (var targetRepository in targets) { var targetRepositoryDisplayName = $"{targetRepository.Owner}/{targetRepository.Repository}"; using (var syncer = new Syncer(targetRepository.Credentials, null, log)) { if (!await syncer.CanSynchronize(targetRepository, syncOutput)) { continue; } var syncContext = await CalculateSyncContext(targetRepository); if (syncContext.Diff.ToBeAddedOrUpdatedEntries.Count() == 0) { log($"Repo {targetRepositoryDisplayName} is in sync"); continue; } var sync = await syncer.Sync(syncContext.Diff, syncOutput, labelsToApplyOnPullRequests, syncContext.Description); var createdSyncBranch = sync.FirstOrDefault(); if (string.IsNullOrEmpty(createdSyncBranch)) { log($"Repo {targetRepositoryDisplayName} is in sync"); } else { log($"Pull created for {targetRepositoryDisplayName}, click here to review and pull: {createdSyncBranch}"); } } } }
public IEnumerable<string> Sync(Diff diff, SyncOutput expectedOutput, IEnumerable<string> labelsToApplyOnPullRequests = null) { var labels = labelsToApplyOnPullRequests == null ? new string[]{ } : labelsToApplyOnPullRequests.ToArray(); if (labels.Any() && expectedOutput != SyncOutput.CreatePullRequest) { throw new InvalidOperationException(string.Format("Labels can only be applied in '{0}' mode.", SyncOutput.CreatePullRequest)); } var t = diff.Transpose(); var branchName = "SyncOMatic-" + DateTimeOffset.UtcNow.ToString("yyyyMMdd-HHmmss"); foreach (var updatesPerOwnerRepositoryBranch in t.Values) { var root = updatesPerOwnerRepositoryBranch.First().Item1.RootTreePart; var tt = new TargetTree(root); foreach (var change in updatesPerOwnerRepositoryBranch) { var source = change.Item2; var destination = change.Item1; tt.Add(destination, source); } var btt = BuildTargetTree(tt); var parentCommit = gw.RootCommitFrom(root); var c = gw.CreateCommit(btt, root.Owner, root.Repository, parentCommit.Sha); switch (expectedOutput) { case SyncOutput.CreateCommit: yield return "https://github.com/" + root.Owner + "/" + root.Repository + "/commit/" + c; break; case SyncOutput.CreateBranch: branchName = gw.CreateBranch(root.Owner, root.Repository, branchName, c); yield return "https://github.com/" + root.Owner + "/" + root.Repository + "/compare/" + UrlSanitize(root.Branch) + "..." + UrlSanitize(branchName); break; case SyncOutput.CreatePullRequest: branchName = gw.CreateBranch(root.Owner, root.Repository, branchName, c); var prNumber = gw.CreatePullRequest(root.Owner, root.Repository, branchName, root.Branch); gw.ApplyLabels(root.Owner, root.Repository, prNumber, labels); yield return "https://github.com/" + root.Owner + "/" + root.Repository + "/pull/" + prNumber; break; default: throw new NotSupportedException(); } } }
/// <summary> /// Processes the record. /// </summary> /// <exception cref="PSInvalidCastException">Incorrect object found.</exception> protected override async Task ProcessRecordAsync() { foreach (string input in Input) { var member = new Member(Prefix, input, true); var output = new SyncOutput(member.Name); if (_currentMembers.ContainsKey(member.Name.ToLower())) { // No changes just add to _postMembers so we don't remove it _postMembers.Add(member.Name.ToLower()); } else { var obj = await FindObjectByName(member); string oldName = null; if (obj == null && Rename.IsPresent) { obj = await FindObjectByIP(member); if (obj != null) { oldName = obj.Name; output.Actions = Actions.Rename; output.Comments = $"Renamed {obj.Name}"; try { if (obj is Network n) { n.Name = member.Name; await n.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } else if (obj is Host h) { h.Name = member.Name; await h.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } } catch (Koopman.CheckPoint.Exceptions.GenericException e) { output.Error = true; WriteError(new ErrorRecord(e, "Rename", ErrorCategory.WriteError, null)); } } } if (obj == null) { output.Actions = Actions.Create; try { if (member.IsHost()) { var host = new Host(Session) { Name = member.Name, Color = Color, Comments = Comments }; if (member.IsIPv4()) { host.IPv4Address = member.IPAddress; } else { host.IPv6Address = member.IPAddress; } host.Groups.Add(GroupName); foreach (string t in Tags ?? Enumerable.Empty <string>()) { host.Tags.Add(t); } await host.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } else { var network = new Network(Session) { Name = member.Name, Color = Color, Comments = Comments }; if (member.IsIPv4()) { network.Subnet4 = member.IPAddress; network.MaskLength4 = member.CIDR; } else { network.Subnet6 = member.IPAddress; network.MaskLength6 = member.CIDR; } network.Groups.Add(GroupName); foreach (string t in Tags ?? Enumerable.Empty <string>()) { network.Tags.Add(t); } await network.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } } catch (Koopman.CheckPoint.Exceptions.GenericException e) { output.Error = true; WriteError(new ErrorRecord(e, "Create", ErrorCategory.WriteError, null)); } } else if (!_currentMembers.ContainsKey(obj.Name.ToLower())) { output.Actions |= Actions.Add; group.Members.Add(obj.Name); try { await group.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } catch (Koopman.CheckPoint.Exceptions.GenericException e) { output.Error = true; WriteError(new ErrorRecord(e, "AddToGroup", ErrorCategory.WriteError, null)); } } // As _postMembers used to work out which entries to remove from the group If // object was renamed enter old name as it could of already been a member Of the // group under the old name _postMembers.Add(((output.Actions.HasFlag(Actions.Rename)) ? oldName : member.Name).ToLower()); } WriteSyncOutput(output); } }
/// <inheritdoc /> protected override async Task EndProcessingAsync() { string[] deleteKeys = _currentMembers.Keys.Except(_postMembers).ToArray(); bool removed = true; if (deleteKeys.Length > 0) { WriteVerbose("Performing bulk remove from group"); foreach (string n in deleteKeys) { group.Members.Remove(n); } try { await group.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } catch (Koopman.CheckPoint.Exceptions.GenericException e) { removed = false; WriteError(new ErrorRecord(e, "RemoveFromGroup", ErrorCategory.WriteError, null)); } } foreach (string key in deleteKeys) { var obj = _currentMembers[key]; var output = new SyncOutput(obj.Name) { Actions = Actions.Remove, Error = !removed }; var used = await Session.FindWhereUsed(identifier : obj.Name, detailLevel : DetailLevels.UID, cancellationToken : CancelProcessToken); if (used.UsedDirectly.Total == 0) { output.Actions |= Actions.Delete; try { if (obj is Host h) { await h.Delete(cancellationToken : CancelProcessToken); } else if (obj is Network n) { await n.Delete(cancellationToken : CancelProcessToken); } } catch (Koopman.CheckPoint.Exceptions.GenericException e) { output.Error = true; WriteError(new ErrorRecord(e, "Delete", ErrorCategory.WriteError, null)); } } else { try { if (obj is Host h) { foreach (string t in Tags) { h.Tags.Remove(t); } await h.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } else if (obj is Network n) { foreach (string t in Tags) { n.Tags.Remove(t); } await n.AcceptChanges(Ignore, cancellationToken : CancelProcessToken); } } catch (Koopman.CheckPoint.Exceptions.GenericException e) { output.Error = true; WriteError(new ErrorRecord(e, "RemoveTags", ErrorCategory.WriteError, null)); } } WriteSyncOutput(output); } }
async Task <string> ProcessUpdates(SyncOutput expectedOutput, IList <Tuple <Parts, IParts> > updatesPerOwnerRepositoryBranch, string[] labels, string description) { var branchName = $"GitHubSync-{DateTimeOffset.UtcNow:yyyyMMdd-HHmmss}"; var root = updatesPerOwnerRepositoryBranch.First().Item1.RootTreePart; var commitSha = string.Empty; var isCollaborator = await gateway.IsCollaborator(root.Owner, root.Repository); if (!isCollaborator) { log("User is not a collaborator, need to create a fork"); if (expectedOutput != SyncOutput.CreatePullRequest) { throw new NotSupportedException($"User is not a collaborator, sync output '{expectedOutput}' is not supported, only creating PRs is supported"); } commitSha = await ProcessUpdatesInFork(root, branchName, updatesPerOwnerRepositoryBranch); } else { commitSha = await ProcessUpdatesInTargetRepository(root, updatesPerOwnerRepositoryBranch); } if (expectedOutput == SyncOutput.CreateCommit) { return($"https://github.com/{root.Owner}/{root.Repository}/commit/{commitSha}"); } if (expectedOutput == SyncOutput.CreateBranch) { branchName = await gateway.CreateBranch(root.Owner, root.Repository, branchName, commitSha); return($"https://github.com/{root.Owner}/{root.Repository}/compare/{UrlSanitize(root.Branch)}...{UrlSanitize(branchName)}"); } if (expectedOutput == SyncOutput.CreatePullRequest || expectedOutput == SyncOutput.MergePullRequest) { var merge = expectedOutput == SyncOutput.MergePullRequest; var prSourceBranch = branchName; if (isCollaborator) { branchName = await gateway.CreateBranch(root.Owner, root.Repository, branchName, commitSha); } else { // Never auto-merge merge = false; var forkedRepository = await gateway.Fork(root.Owner, root.Repository); prSourceBranch = $"{forkedRepository.Owner.Login}:{prSourceBranch}"; } var prNumber = await gateway.CreatePullRequest(root.Owner, root.Repository, prSourceBranch, root.Branch, merge, description); if (isCollaborator) { await gateway.ApplyLabels(root.Owner, root.Repository, prNumber, labels); } return($"https://github.com/{root.Owner}/{root.Repository}/pull/{prNumber}"); } throw new NotSupportedException(); }