From 0b2168c425fc848bf495831ccf7ba8cc092b24ed Mon Sep 17 00:00:00 2001 From: Summer Warren Date: Tue, 16 Sep 2025 13:06:30 -0700 Subject: [PATCH 1/4] Update MCP to reuse branch for SDK generation --- .../Mocks/Services/MockDevOpsService.cs | 2 +- .../Tools/SpecWorkFlowToolTests.cs | 8 +++++--- .../Services/DevOpsService.cs | 5 +++-- .../Tools/ReleasePlan/SpecWorkFlowTool.cs | 20 ++++++++++++++++++- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs index d584ff9e6d2..40a6e86ea69 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs @@ -97,7 +97,7 @@ Task IDevOpsService.LinkNamespaceApprovalIssueAsync(int releasePlanWorkIte return Task.FromResult(true); } - Task IDevOpsService.RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId) + Task IDevOpsService.RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch) { throw new NotImplementedException(); } diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs index e44cb9faf93..7cd9bb6f6eb 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs @@ -193,7 +193,7 @@ public async Task GenerateSdk_Uses_WorkItemApi() .ReturnsAsync( new Octokit.PullRequest(123, null, null, null, null, null, null, null, 123, ItemState.Open, null, null, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, null, null, null, null, null, null, false, null, null, null, null, 0, 1, 1, 1, 1, null, false, null, null, null, labels, null)); - mockDevOpsService.Setup(x => x.RunSDKGenerationPipelineAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + mockDevOpsService.Setup(x => x.RunSDKGenerationPipelineAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new Build() { Id = 100, @@ -208,6 +208,8 @@ public async Task GenerateSdk_Uses_WorkItemApi() pullRequestNumber: 123, workItemId: 456 ); + + Assert.That(result.ToString(), Does.Contain("Azure DevOps pipeline https://dev.azure.com/azure-sdk/internal/_build/results?buildId=100 has been initiated to generate the SDK. Build ID is 100")); } @@ -219,7 +221,7 @@ public async Task GenerateSdk_Without_pr_and_workitem() mockTypeSpecHelper.Setup(x => x.IsValidTypeSpecProjectPath(It.IsAny())) .Returns(true); - mockDevOpsService.Setup(x => x.RunSDKGenerationPipelineAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + mockDevOpsService.Setup(x => x.RunSDKGenerationPipelineAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new Build() { Id = 100, @@ -243,7 +245,7 @@ public async Task GenerateSdk_With_pr_and_no_workitem() mockTypeSpecHelper.Setup(x => x.IsValidTypeSpecProjectPath(It.IsAny())) .Returns(true); - mockDevOpsService.Setup(x => x.RunSDKGenerationPipelineAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + mockDevOpsService.Setup(x => x.RunSDKGenerationPipelineAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new Build() { Id = 100, diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs index 0fd502d456c..36b0b8d82ad 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs @@ -89,7 +89,7 @@ public interface IDevOpsService public Task GetReleasePlanForWorkItemAsync(int workItemId); public Task GetReleasePlanAsync(string pullRequestUrl); public Task CreateReleasePlanWorkItemAsync(ReleasePlanDetails releasePlan); - public Task RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId); + public Task RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch = "main"); public Task GetPipelineRunAsync(int buildId); public Task GetSDKPullRequestFromPipelineRunAsync(int buildId, string language, int workItemId); public Task AddSdkInfoInReleasePlanAsync(int workItemId, string language, string sdkGenerationPipelineUrl, string sdkPullRequestUrl); @@ -540,7 +540,7 @@ public static bool IsSDKGenerationSupported(string language) }; } - public async Task RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId) + public async Task RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch = "main") { int pipelineDefinitionId = GetPipelineDefinitionId(language); if (pipelineDefinitionId == 0) @@ -550,6 +550,7 @@ public async Task RunSDKGenerationPipelineAsync(string branchRef, string var templateParams = new Dictionary { + { "SdkRepoBranch", sdkRepoBranch}, { "ConfigType", "TypeSpec"}, { "ConfigPath", $"{typespecProjectRoot}/tspconfig.yaml" }, { "ApiVersion", apiVersion }, diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs index 5d08c41361c..2117a3dfa4f 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs @@ -331,8 +331,26 @@ public async Task RunGenerateSdkAsync(string typespecProjec var pullRequest = await githubService.GetPullRequestAsync(REPO_OWNER, PUBLIC_SPECS_REPO, pullRequestNumber); branchRef = (pullRequest?.Merged ?? false) ? pullRequest.Base.Ref : $"refs/pull/{pullRequestNumber}/merge"; } + + string sdkRepoBranch = "main"; + var releasePlan = workItemId != 0 ? await devopsService.GetReleasePlanForWorkItemAsync(workItemId) : null; + var sdkPullRequestUrl = releasePlan?.SDKInfo.FirstOrDefault(s => s.Language == language)?.SdkPullRequestUrl; + if (!string.IsNullOrEmpty(sdkPullRequestUrl)) + { + var uri = new Uri(sdkPullRequestUrl); + var segments = uri.Segments; + string repoOwner = segments[1].TrimEnd('/'); + string repoName = segments[2].TrimEnd('/'); + int sdkPullRequestNumber = int.Parse(segments[4].TrimEnd('/')); + var sdkPullRequest = await githubService.GetPullRequestAsync(repoOwner, repoName, sdkPullRequestNumber); + if (sdkPullRequest is not null && sdkPullRequest.State != "closed" && sdkPullRequest.Merged == false) + { + sdkRepoBranch = sdkPullRequest.Head.Ref; + } + } + logger.LogInformation("Running SDK generation pipeline"); - var pipelineRun = await devopsService.RunSDKGenerationPipelineAsync(branchRef, typeSpecProjectPath, apiVersion, sdkReleaseType, language, workItemId); + var pipelineRun = await devopsService.RunSDKGenerationPipelineAsync(branchRef, typeSpecProjectPath, apiVersion, sdkReleaseType, language, workItemId, sdkRepoBranch); response = new SDKWorkflowResponse() { Status = "Success", From 13a7715d52aa693a323f6ebbc72806fef30ac8cf Mon Sep 17 00:00:00 2001 From: Summer Warren Date: Tue, 16 Sep 2025 13:50:39 -0700 Subject: [PATCH 2/4] Reuse ParseSDKPullRequestUrl --- .../Tools/ReleasePlan/SpecWorkFlowTool.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs index 2117a3dfa4f..0670255424b 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/ReleasePlan/SpecWorkFlowTool.cs @@ -337,12 +337,8 @@ public async Task RunGenerateSdkAsync(string typespecProjec var sdkPullRequestUrl = releasePlan?.SDKInfo.FirstOrDefault(s => s.Language == language)?.SdkPullRequestUrl; if (!string.IsNullOrEmpty(sdkPullRequestUrl)) { - var uri = new Uri(sdkPullRequestUrl); - var segments = uri.Segments; - string repoOwner = segments[1].TrimEnd('/'); - string repoName = segments[2].TrimEnd('/'); - int sdkPullRequestNumber = int.Parse(segments[4].TrimEnd('/')); - var sdkPullRequest = await githubService.GetPullRequestAsync(repoOwner, repoName, sdkPullRequestNumber); + var parsedUrl = DevOpsService.ParseSDKPullRequestUrl(sdkPullRequestUrl); + var sdkPullRequest = await githubService.GetPullRequestAsync(parsedUrl.RepoOwner, parsedUrl.RepoName, parsedUrl.PrNumber); if (sdkPullRequest is not null && sdkPullRequest.State != "closed" && sdkPullRequest.Merged == false) { sdkRepoBranch = sdkPullRequest.Head.Ref; From fb0438306f2b833031d69ab8d79d29f63bac7e77 Mon Sep 17 00:00:00 2001 From: Summer Date: Tue, 16 Sep 2025 13:10:15 -0700 Subject: [PATCH 3/4] Update tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs index 7cd9bb6f6eb..35c0c5b2064 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs @@ -209,8 +209,7 @@ public async Task GenerateSdk_Uses_WorkItemApi() workItemId: 456 ); - - Assert.That(result.ToString(), Does.Contain("Azure DevOps pipeline https://dev.azure.com/azure-sdk/internal/_build/results?buildId=100 has been initiated to generate the SDK. Build ID is 100")); + Assert.That(result, Does.Contain("Azure DevOps pipeline https://dev.azure.com/azure-sdk/internal/_build/results?buildId=100 has been initiated to generate the SDK. Build ID is 100")); } [Test] From 412dfe3316e82fbc293b83ce3b234483f218ab05 Mon Sep 17 00:00:00 2001 From: Summer Warren Date: Thu, 18 Sep 2025 14:32:15 -0700 Subject: [PATCH 4/4] Remove default branch name and differentiate api spec branch name --- .../Mocks/Services/MockDevOpsService.cs | 4 ++-- .../Tools/SpecWorkFlowToolTests.cs | 2 +- .../Azure.Sdk.Tools.Cli/Services/DevOpsService.cs | 12 ++++++------ .../Tools/ReleasePlan/SpecWorkFlowTool.cs | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs index 40a6e86ea69..6b8510256c9 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Mocks/Services/MockDevOpsService.cs @@ -13,7 +13,7 @@ public Task GetPackageWorkItemAsync(string packageName, string throw new NotImplementedException(); } - public Task RunPipelineAsync(int pipelineDefinitionId, Dictionary templateParams, string branchRef = "main") + public Task RunPipelineAsync(int pipelineDefinitionId, Dictionary templateParams, string apiSpecBranchRef = "main") { return Task.FromResult(new Build { @@ -97,7 +97,7 @@ Task IDevOpsService.LinkNamespaceApprovalIssueAsync(int releasePlanWorkIte return Task.FromResult(true); } - Task IDevOpsService.RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch) + Task IDevOpsService.RunSDKGenerationPipelineAsync(string apiSpecBranchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch) { throw new NotImplementedException(); } diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs index 35c0c5b2064..3969d65c7e0 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli.Tests/Tools/SpecWorkFlowToolTests.cs @@ -209,7 +209,7 @@ public async Task GenerateSdk_Uses_WorkItemApi() workItemId: 456 ); - Assert.That(result, Does.Contain("Azure DevOps pipeline https://dev.azure.com/azure-sdk/internal/_build/results?buildId=100 has been initiated to generate the SDK. Build ID is 100")); + Assert.That(result.ToString(), Does.Contain("Azure DevOps pipeline https://dev.azure.com/azure-sdk/internal/_build/results?buildId=100 has been initiated to generate the SDK. Build ID is 100")); } [Test] diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs index 36b0b8d82ad..951925d85c1 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Cli/Services/DevOpsService.cs @@ -89,7 +89,7 @@ public interface IDevOpsService public Task GetReleasePlanForWorkItemAsync(int workItemId); public Task GetReleasePlanAsync(string pullRequestUrl); public Task CreateReleasePlanWorkItemAsync(ReleasePlanDetails releasePlan); - public Task RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch = "main"); + public Task RunSDKGenerationPipelineAsync(string apiSpecBranchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch = ""); public Task GetPipelineRunAsync(int buildId); public Task GetSDKPullRequestFromPipelineRunAsync(int buildId, string language, int workItemId); public Task AddSdkInfoInReleasePlanAsync(int workItemId, string language, string sdkGenerationPipelineUrl, string sdkPullRequestUrl); @@ -98,7 +98,7 @@ public interface IDevOpsService public Task UpdateSpecPullRequestAsync(int releasePlanWorkItemId, string specPullRequest); public Task LinkNamespaceApprovalIssueAsync(int releasePlanWorkItemId, string url); public Task GetPackageWorkItemAsync(string packageName, string language, string packageVersion = ""); - public Task RunPipelineAsync(int pipelineDefinitionId, Dictionary templateParams, string branchRef = "main"); + public Task RunPipelineAsync(int pipelineDefinitionId, Dictionary templateParams, string apiSpecBranchRef = "main"); public Task>> GetPipelineLlmArtifacts(string project, int buildId); } @@ -540,7 +540,7 @@ public static bool IsSDKGenerationSupported(string language) }; } - public async Task RunSDKGenerationPipelineAsync(string branchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch = "main") + public async Task RunSDKGenerationPipelineAsync(string apiSpecBranchRef, string typespecProjectRoot, string apiVersion, string sdkReleaseType, string language, int workItemId, string sdkRepoBranch = "") { int pipelineDefinitionId = GetPipelineDefinitionId(language); if (pipelineDefinitionId == 0) @@ -558,7 +558,7 @@ public async Task RunSDKGenerationPipelineAsync(string branchRef, string { "CreatePullRequest", "true" }, { "ReleasePlanWorkItemId", $"{workItemId}"} }; - var build = await RunPipelineAsync(pipelineDefinitionId, templateParams, branchRef); + var build = await RunPipelineAsync(pipelineDefinitionId, templateParams, apiSpecBranchRef); var pipelineRunUrl = GetPipelineUrl(build.Id); logger.LogInformation($"Started pipeline run {pipelineRunUrl} to generate SDK."); if (workItemId != 0) @@ -570,7 +570,7 @@ public async Task RunSDKGenerationPipelineAsync(string branchRef, string return build; } - public async Task RunPipelineAsync(int pipelineDefinitionId, Dictionary templateParams, string branchRef = "main") + public async Task RunPipelineAsync(int pipelineDefinitionId, Dictionary templateParams, string apiSpecBranchRef = "main") { if (pipelineDefinitionId == 0) { @@ -588,7 +588,7 @@ public async Task RunPipelineAsync(int pipelineDefinitionId, Dictionary RunGenerateSdkAsync(string typespecProjec } string typeSpecProjectPath = typespecHelper.GetTypeSpecProjectRelativePath(typespecProjectRoot); - string branchRef = "main"; + string apiSpecBranchRef = "main"; if (pullRequestNumber > 0) { var pullRequest = await githubService.GetPullRequestAsync(REPO_OWNER, PUBLIC_SPECS_REPO, pullRequestNumber); - branchRef = (pullRequest?.Merged ?? false) ? pullRequest.Base.Ref : $"refs/pull/{pullRequestNumber}/merge"; + apiSpecBranchRef = (pullRequest?.Merged ?? false) ? pullRequest.Base.Ref : $"refs/pull/{pullRequestNumber}/merge"; } - string sdkRepoBranch = "main"; + string sdkRepoBranch = ""; var releasePlan = workItemId != 0 ? await devopsService.GetReleasePlanForWorkItemAsync(workItemId) : null; var sdkPullRequestUrl = releasePlan?.SDKInfo.FirstOrDefault(s => s.Language == language)?.SdkPullRequestUrl; if (!string.IsNullOrEmpty(sdkPullRequestUrl)) @@ -346,7 +346,7 @@ public async Task RunGenerateSdkAsync(string typespecProjec } logger.LogInformation("Running SDK generation pipeline"); - var pipelineRun = await devopsService.RunSDKGenerationPipelineAsync(branchRef, typeSpecProjectPath, apiVersion, sdkReleaseType, language, workItemId, sdkRepoBranch); + var pipelineRun = await devopsService.RunSDKGenerationPipelineAsync(apiSpecBranchRef, typeSpecProjectPath, apiVersion, sdkReleaseType, language, workItemId, sdkRepoBranch); response = new SDKWorkflowResponse() { Status = "Success",