Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 95 additions & 47 deletions src/SourceBuild/content/eng/tools/CreateBaselineUpdatePR/PRCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,72 +280,120 @@ private async Task<TreeResponse> CreateParentTreeAsync(TreeResponse testResultsT

private async Task CreateOrUpdatePullRequestAsync(TreeResponse parentTreeResponse, int buildId, string title, string targetBranch)
{
// Look for a pre-existing pull request
var request = new PullRequestRequest
{
Base = targetBranch
};
var existingPullRequest = await _client.PullRequest.GetAllForRepository(_repoOwner, _repoName, request);
var matchingPullRequest = existingPullRequest.FirstOrDefault(pr => pr.Title == title);
var existingPullRequest = await GetExistingPullRequestAsync(title, targetBranch);

// Create the branch name and get the head reference
string newBranchName = string.Empty;
Reference? headReference = null;
if (matchingPullRequest == null)
string headSha = await GetHeadShaAsync(targetBranch);
if (existingPullRequest == null)
{
string utcTime = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
newBranchName = $"pr-baseline-{utcTime}";
headReference = await _client.Git.Reference.Get(_repoOwner, _repoName, "heads/" + targetBranch);
}
else
{
newBranchName = matchingPullRequest.Head.Ref;
headReference = await _client.Git.Reference.Get(_repoOwner, _repoName, "heads/" + matchingPullRequest.Head.Ref);
}

// Create the commit
string commitMessage = $"Update baselines for build {BuildLink}{buildId} (internal Microsoft link)";
var newCommit = new NewCommit(commitMessage, parentTreeResponse.Sha, headReference.Object.Sha);
var commitResponse = await _client.Git.Commit.Create(_repoOwner, _repoName, newCommit);

string pullRequestBody = $"This PR was created by the `CreateBaselineUpdatePR` tool for build {buildId}. \n\n" +
$"The updated test results can be found at {BuildLink}{buildId} (internal Microsoft link)";
if (matchingPullRequest != null)
{
// Update the existing pull request with the new commit
var referenceUpdate = new ReferenceUpdate(commitResponse.Sha);
await _client.Git.Reference.Update(_repoOwner, _repoName, $"heads/{newBranchName}", referenceUpdate);
newBranchName = existingPullRequest.Head.Ref;

// Update the body of the pull request
var pullRequestUpdate = new PullRequestUpdate
try
{
Body = pullRequestBody
};
await _client.PullRequest.Update(_repoOwner, _repoName, matchingPullRequest.Number, pullRequestUpdate);
// Merge the target branch into the existing pull request
var merge = new NewMerge(newBranchName, headSha);
await _client.Repository.Merging.Create(_repoOwner, _repoName, merge);
}
catch (Exception e)
{
Log.LogWarning($"Failed to merge the target branch into the existing pull request: {e.Message}");
Log.LogWarning("Continuing with updating the existing pull request. You may need to resolve conflicts manually in the PR.");
}

Log.LogInformation($"Updated existing pull request #{matchingPullRequest.Number}. URL: {matchingPullRequest.HtmlUrl}");
headSha = await GetHeadShaAsync(newBranchName);
}
else

var commitSha = await CreateCommitAsync(parentTreeResponse.Sha, headSha, $"Update baselines for build {BuildLink}{buildId} (internal Microsoft link)");
if (await ShouldMakeUpdatesAsync(headSha, commitSha))
{
var comparison = await _client.Repository.Commit.Compare(_repoOwner, _repoName, headReference.Object.Sha, commitResponse.Sha);
if (!comparison.Files.Any())
string pullRequestBody = $"This PR was created by the `CreateBaselineUpdatePR` tool for build {buildId}. \n\n" +
$"The updated test results can be found at {BuildLink}{buildId} (internal Microsoft link)";
if (existingPullRequest != null)
{
// No changes to commit
Log.LogInformation("No changes to commit. Skipping PR creation.");
return;
await UpdatePullRequestAsync(newBranchName, commitSha, pullRequestBody, existingPullRequest);
}
else
{
await CreatePullRequestAsync(newBranchName, commitSha, targetBranch, title, pullRequestBody);
}
}
}

// Create a new pull request
var newReference = new NewReference("refs/heads/" + newBranchName, commitResponse.Sha);
await _client.Git.Reference.Create(_repoOwner, _repoName, newReference);
private async Task<PullRequest?> GetExistingPullRequestAsync(string title, string targetBranch)
{
var request = new PullRequestRequest
{
Base = targetBranch
};
var existingPullRequest = await _client.PullRequest.GetAllForRepository(_repoOwner, _repoName, request);
return existingPullRequest.FirstOrDefault(pr => pr.Title == title);
}

var newPullRequest = new NewPullRequest(title, newBranchName, targetBranch)
{
Body = pullRequestBody
};
var pullRequest = await _client.PullRequest.Create(_repoOwner, _repoName, newPullRequest);
private async Task<string> CreateCommitAsync(string newSha, string headSha, string commitMessage)
{
var newCommit = new NewCommit(commitMessage, newSha, headSha);
var commit = await _client.Git.Commit.Create(_repoOwner, _repoName, newCommit);
return commit.Sha;
}

Log.LogInformation($"Created pull request #{pullRequest.Number}. URL: {pullRequest.HtmlUrl}");
private async Task<bool> ShouldMakeUpdatesAsync(string headSha, string commitSha)
{
var comparison = await _client.Repository.Commit.Compare(_repoOwner, _repoName, headSha, commitSha);
if (!comparison.Files.Any())
{
Log.LogInformation("No changes to commit. Skipping PR creation/updates.");
return false;
}
return true;
}

private async Task UpdatePullRequestAsync(string branchName, string commitSha, string body, PullRequest pullRequest)
{
await UpdateReferenceAsync(branchName, commitSha);

var pullRequestUpdate = new PullRequestUpdate
{
Body = body
};
await _client.PullRequest.Update(_repoOwner, _repoName, pullRequest.Number, pullRequestUpdate);

Log.LogInformation($"Updated existing pull request #{pullRequest.Number}. URL: {pullRequest.HtmlUrl}");
}

private async Task CreatePullRequestAsync(string newBranchName, string commitSha, string targetBranch, string title, string body)
{
await CreateReferenceAsync(newBranchName, commitSha);

var newPullRequest = new NewPullRequest(title, newBranchName, targetBranch)
{
Body = body
};
var pullRequest = await _client.PullRequest.Create(_repoOwner, _repoName, newPullRequest);

Log.LogInformation($"Created pull request #{pullRequest.Number}. URL: {pullRequest.HtmlUrl}");
}

private async Task<string> GetHeadShaAsync(string branchName)
{
var reference = await _client.Git.Reference.Get(_repoOwner, _repoName, $"heads/{branchName}");
return reference.Object.Sha;
}

private async Task UpdateReferenceAsync(string branchName, string commitSha)
{
var referenceUpdate = new ReferenceUpdate(commitSha);
await _client.Git.Reference.Update(_repoOwner, _repoName, $"heads/{branchName}", referenceUpdate);
}

private async Task CreateReferenceAsync(string branchName, string commitSha)
{
var newReference = new NewReference($"refs/heads/{branchName}", commitSha);
await _client.Git.Reference.Create(_repoOwner, _repoName, newReference);
}
}