private static async Task MakeGithubPr( GithubMergeTool.GithubMergeTool gh, string repoOwner, string repoName, string srcBranch, string destBranch, bool addAutoMergeLabel, bool isAutomatedRun) { Console.WriteLine($"Merging {repoName} from {srcBranch} to {destBranch}"); var(prCreated, error) = await gh.CreateMergePr(repoOwner, repoName, srcBranch, destBranch, addAutoMergeLabel, isAutomatedRun); if (prCreated) { Console.WriteLine("PR created successfully"); } else if (error == null) { Console.WriteLine("PR creation skipped. PR already exists or all commits are present in base branch"); } else { Console.WriteLine($"##vso[task.logissue type=error]Error creating PR. GH response code: {error.StatusCode}"); Console.WriteLine($"##vso[task.logissue type=error]{await error.Content.ReadAsStringAsync()}"); } }
// Returns 'true' if all PRs were created/updated successfully. private static async Task <bool> RunAsync(XDocument config, bool isAutomatedRun, bool isDryRun, string githubToken) { // Since this is run on AzDO as an automated cron pipeline, times are in UTC. // See https://docs.microsoft.com/en-us/azure/devops/pipelines/build/triggers?view=azure-devops&tabs=yaml#scheduled-triggers var runDateTime = DateTime.UtcNow; var allSuccess = true; var gh = new GithubMergeTool.GithubMergeTool("*****@*****.**", githubToken, isDryRun); foreach (var repo in config.Root.Elements("repo")) { var owner = repo.Attribute("owner").Value; var mergeOwners = repo.Attribute("mergeOwners")?.Value.Split(',').ToList() ?? new List <string>(); var name = repo.Attribute("name").Value; // We don't try to update existing PR unless asked. var updateExistingPr = bool.Parse(repo.Attribute("updateExistingPr")?.Value ?? "false"); foreach (var merge in repo.Elements("merge")) { var fromBranch = merge.Attribute("from").Value; var toBranch = merge.Attribute("to").Value; if (!ShouldRunMerge(merge, isAutomatedRun, runDateTime)) { continue; } var prOwners = merge.Attribute("owners")?.Value.Split(',').ToList() ?? mergeOwners; var addAutoMergeLabel = bool.Parse(merge.Attribute("addAutoMergeLabel")?.Value ?? "true"); try { var(success, shouldContinue) = await MakeGithubPr(gh, owner, name, prOwners, fromBranch, toBranch, updateExistingPr, addAutoMergeLabel, isAutomatedRun, isDryRun, githubToken); allSuccess = allSuccess && success; if (!shouldContinue) { return(false); } } catch (Exception ex) { Console.WriteLine("##vso[task.logissue type=error]Error creating merge PR.", ex); } finally { // Delay in order to avoid triggering GitHub rate limiting await Task.Delay(4000); } } } return(allSuccess); }
/// <summary> /// Make requests to github to merge a source branch into a destination branch. /// </summary> private static async Task <(bool success, bool shouldContinue)> MakeGithubPr( GithubMergeTool.GithubMergeTool gh, string repoOwner, string repoName, List <string> prOwners, string srcBranch, string destBranch, bool updateExistingPr, bool addAutoMergeLabel, bool isAutomatedRun, bool isDryRun, string githubToken) { Console.WriteLine($"Merging {repoName} from {srcBranch} to {destBranch}"); var(prCreated, error) = await gh.CreateMergePr(repoOwner, repoName, prOwners, srcBranch, destBranch, updateExistingPr, addAutoMergeLabel, isAutomatedRun); if (prCreated) { Console.WriteLine("PR created successfully"); } else if (error == null) { Console.WriteLine("PR creation skipped. PR already exists or all commits are present in base branch"); } else { var errorStatus = error.StatusCode; var isWarning = errorStatus == HttpStatusCode.UnprocessableEntity || errorStatus == HttpStatusCode.NotFound; var issueType = isWarning ? "warning" : "error"; Console.WriteLine($"##vso[task.logissue type={issueType}]Error creating PR. GH response code: {error.StatusCode}"); Console.WriteLine($"##vso[task.logissue type={issueType}]{await error.Content.ReadAsStringAsync()}"); // Github rate limits are much lower for unauthenticated users. We will definitely hit a rate limit // If we hit it during a dryrun, just bail out. if (isDryRun && string.IsNullOrWhiteSpace(githubToken)) { if (TryGetRemainingRateLimit(error.Headers, out var remainingRateLimit) && remainingRateLimit == 0) { Console.WriteLine($"##vso[task.logissue type=error]Hit GitHub rate limit in dryrun with no auth token. Bailing out."); return(success : false, shouldContinue : false); } } return(success : isWarning, shouldContinue : true); } return(success : true, shouldContinue : true); }
/// <summary> /// Make requests to github to merge a source branch into a destination branch. /// </summary> /// <returns>true if we encounter a recoverable error, false if unrecoverable.</returns> private static async Task <bool> MakeGithubPr( GithubMergeTool.GithubMergeTool gh, string repoOwner, string repoName, string srcBranch, string destBranch, bool addAutoMergeLabel, bool isAutomatedRun, bool isDryRun, string githubToken) { Console.WriteLine($"Merging {repoName} from {srcBranch} to {destBranch}"); var(prCreated, error) = await gh.CreateMergePr(repoOwner, repoName, srcBranch, destBranch, addAutoMergeLabel, isAutomatedRun); if (prCreated) { Console.WriteLine("PR created successfully"); } else if (error == null) { Console.WriteLine("PR creation skipped. PR already exists or all commits are present in base branch"); } else { Console.WriteLine($"##vso[task.logissue type=error]Error creating PR. GH response code: {error.StatusCode}"); Console.WriteLine($"##vso[task.logissue type=error]{await error.Content.ReadAsStringAsync()}"); // Github rate limits are much lower for unauthenticated users. We will definitely hit a rate limit // If we hit it during a dryrun, just bail out. if (isDryRun && string.IsNullOrWhiteSpace(githubToken)) { if (TryGetRemainingRateLimit(error.Headers, out var remainingRateLimit) && remainingRateLimit == 0) { Console.WriteLine($"##vso[task.logissue type=error]Hit GitHub rate limit in dryrun with no auth token. Bailing out."); return(false); } } } return(true); }
private static async Task RunAsync(XDocument config, bool isAutomatedRun, bool isDryRun, string githubToken) { var gh = new GithubMergeTool.GithubMergeTool("*****@*****.**", githubToken, isDryRun); foreach (var repo in config.Root.Elements("repo")) { var owner = repo.Attribute("owner").Value; var name = repo.Attribute("name").Value; foreach (var merge in repo.Elements("merge")) { var fromBranch = merge.Attribute("from").Value; var toBranch = merge.Attribute("to").Value; var frequency = merge.Attribute("frequency")?.Value; if (isAutomatedRun && frequency == "weekly" && DateTime.Now.DayOfWeek != DayOfWeek.Sunday) { continue; } var addAutoMergeLabel = bool.Parse(merge.Attribute("addAutoMergeLabel")?.Value ?? "true"); try { bool shouldContinue = await MakeGithubPr(gh, owner, name, fromBranch, toBranch, addAutoMergeLabel, isAutomatedRun, isDryRun, githubToken); if (!shouldContinue) { return; } } catch (Exception ex) { Console.WriteLine("##vso[task.logissue type=error]Error creating merge PR.", ex); } finally { // Delay in order to avoid triggering GitHub rate limiting await Task.Delay(4000); } } } }