Repository: Azure/azure-rest-api-specs
Pull request #34393: Support multiple packages in Spec-Gen-SDK
Author: login: chidozieononiwu
Labels: name: PipelineBotTrigger
Created: 2025-05-01T01:26:38Z | Merged: 2025-05-09T22:08:56Z

replaces https://github.com/Azure/azure-rest-api-specs/pull/34103


Diff:
diff --git a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
index 50904dca4754..5b49866b4052 100644
--- a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
+++ b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
@@ -197,17 +197,6 @@ extends:
                 $optional_params || true # return true always to suppress exit error logging
             displayName: 'Generate SDK'
 
-          - task: Powershell@2
-            inputs:
-              filePath: $(SdkRepoDirectory)/eng/common/scripts/Save-Package-Properties.ps1
-              arguments: >
-                -ServiceDirectory $(GeneratedSDK.ServiceName)
-                -OutDirectory $(GeneratedSDK.StagedArtifactsFolder)/PackageInfo
-              pwsh: true
-              workingDirectory: $(SdkRepoDirectory)
-            displayName: Dump Package properties
-            condition: and(succeeded(), ne(variables['GeneratedSDK.ServiceName'], ''), ne(variables['GeneratedSDK.StagedArtifactsFolder'], ''))
-
           - template: /eng/common/pipelines/templates/steps/publish-1es-artifact.yml
             parameters:
               ArtifactName: $(SpecGenSdkArtifactName)
@@ -217,8 +206,8 @@ extends:
           - template: /eng/common/pipelines/templates/steps/publish-1es-artifact.yml
             parameters:
               ArtifactName: $(PipelineArtifactsName)
-              ArtifactPath: $(GeneratedSDK.StagedArtifactsFolder)
-              CustomCondition: and(succeeded(), ne(variables['GeneratedSDK.StagedArtifactsFolder'], ''))
+              ArtifactPath: $(StagedArtifactsFolder)
+              CustomCondition: and(succeeded(), ne(variables['StagedArtifactsFolder'], ''))
 
           - ${{ if eq(variables['System.TeamProject'], 'internal') }}:
             - task: PowerShell@2
@@ -279,9 +268,15 @@ extends:
                     -PRBody "$(GeneratedSDKInformation)"
                     -OpenAsDraft $true
 
-            - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
-              - template: /eng/common/pipelines/templates/steps/detect-api-changes.yml
-                parameters:
-                  ArtifactPath: $(GeneratedSDK.StagedArtifactsFolder)
-                  ArtifactName: $(PipelineArtifactsName)
-                  RepoRoot: $(SdkRepoDirectory)
+          - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
+            - pwsh: |
+                . $(SpecRepoDirectory)/eng/common/scripts/Helpers/ApiView-Helpers.ps1
+                Create-API-Review `
+                  -specGenSDKArtifactPath "$(System.DefaultWorkingDirectory)/$(SpecGenSdkArtifactPath)/$(SpecGenSdkArtifactName).json" `
+                  -apiviewArtifactName "$(PipelineArtifactsName)" `
+                  -buildId "$(Build.BuildId)" `
+                  -commitish "$(System.PullRequest.SourceCommitId)" `
+                  -repoName "$(SdkRepoOwner)/$(SpecRepoName)" `
+                  -pullRequestNumber "$(System.PullRequest.PullRequestNumber)" 
+              displayName: 'Detect API Change and Create API Review'
+              condition: and(succeeded(), ne(variables['SpecGenSdkArtifactPath'], ''), ne(variables['SpecGenSdkArtifactName'], ''))
\ No newline at end of file
diff --git a/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts b/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
index 9fec9c892aa1..8ad1a1fd3670 100644
--- a/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
+++ b/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
 import { findReadmeFiles, getArgumentValue, getAllTypeSpecPaths, objectToMap } from "./utils.js";
 import { LogIssueType, LogLevel, logMessage, setVsoVariable, vsoLogIssue } from "./log.js";
 import {
+  APIViewRequestData,
   SdkName,
   SpecGenSdkArtifactInfo,
   SpecGenSdkCmdInput,
@@ -240,6 +241,8 @@ export function generateArtifact(
   breakingChangeLabel: string,
   hasBreakingChange: boolean,
   hasManagementPlaneSpecs: boolean,
+  stagedArtifactsFolder: string,
+  apiViewRequestData: APIViewRequestData []
 ): number {
   const specGenSdkArtifactName = "spec-gen-sdk-artifact";
   const specGenSdkArtifactFileName = specGenSdkArtifactName + ".json";
@@ -259,6 +262,7 @@ export function generateArtifact(
       labelAction: hasBreakingChange,
       isSpecGenSdkCheckRequired:
         hasManagementPlaneSpecs && SpecGenSdkRequiredSettings[commandInput.sdkLanguage as SdkName],
+      apiViewRequestData: apiViewRequestData
     };
     fs.writeFileSync(
       path.join(commandInput.workingFolder, specGenSdkArtifactPath, specGenSdkArtifactFileName),
@@ -266,6 +270,7 @@ export function generateArtifact(
     );
     setVsoVariable("SpecGenSdkArtifactName", specGenSdkArtifactName);
     setVsoVariable("SpecGenSdkArtifactPath", specGenSdkArtifactPath);
+    setVsoVariable("StagedArtifactsFolder", stagedArtifactsFolder);
     setVsoVariable("BreakingChangeLabelAction", hasBreakingChange ? "add" : "remove");
     setVsoVariable("BreakingChangeLabel", breakingChangeLabel);
   } catch (error) {
diff --git a/eng/tools/spec-gen-sdk-runner/src/commands.ts b/eng/tools/spec-gen-sdk-runner/src/commands.ts
index aec2f386aae4..5a992155f5ae 100644
--- a/eng/tools/spec-gen-sdk-runner/src/commands.ts
+++ b/eng/tools/spec-gen-sdk-runner/src/commands.ts
@@ -2,7 +2,7 @@ import fs from "node:fs";
 import path from "node:path";
 import { runSpecGenSdkCommand, resetGitRepo } from "./utils.js";
 import { LogLevel, logMessage, vsoAddAttachment, vsoLogIssue } from "./log.js";
-import { SpecGenSdkCmdInput } from "./types.js";
+import { APIViewRequestData, SpecGenSdkCmdInput } from "./types.js";
 import { detectChangedSpecConfigFiles } from "./change-files.js";
 import {
   generateArtifact,
@@ -86,6 +86,8 @@ export async function generateSdkForSpecPr(): Promise<number> {
   let currentRunHasBreakingChange = false;
   let overallExecutionResult = "";
   let currentExecutionResult = "";
+  let stagedArtifactsFolder = "";
+  const apiViewRequestData: APIViewRequestData [] = [];
 
   for (const changedSpec of changedSpecs) {
     if (!changedSpec.typespecProject && !changedSpec.readmeMd) {
@@ -130,6 +132,24 @@ export async function generateSdkForSpecPr(): Promise<number> {
       // Read the execution report to aggreate the generation results
       executionReport = getExecutionReport(commandInput);
       currentExecutionResult = executionReport.executionResult;
+
+      if (executionReport.stagedArtifactsFolder) {
+        stagedArtifactsFolder = executionReport.stagedArtifactsFolder;
+      }
+
+      if (executionReport.stagedArtifactsFolder && executionReport.sdkApiViewArtifactFolder) {
+        const apiViewArtifactRelPath = path.relative(executionReport.stagedArtifactsFolder, executionReport.sdkApiViewArtifactFolder);
+        for (const pkg of executionReport.packages) {
+          if (pkg.apiViewArtifact) {
+            const fileName = path.basename(pkg.apiViewArtifact);
+            apiViewRequestData.push({
+              packageName: pkg.packageName,
+              filePath: path.join(apiViewArtifactRelPath, fileName),
+            });
+          }
+        }
+      }
+
       if (overallExecutionResult !== "failed") {
         overallExecutionResult = currentExecutionResult;
       }
@@ -152,6 +172,8 @@ export async function generateSdkForSpecPr(): Promise<number> {
       breakingChangeLabel,
       overallRunHasBreakingChange,
       hasManagementPlaneSpecs,
+      stagedArtifactsFolder,
+      apiViewRequestData
     ) || statusCode;
   return statusCode;
 }
diff --git a/eng/tools/spec-gen-sdk-runner/src/types.ts b/eng/tools/spec-gen-sdk-runner/src/types.ts
index 75a926406c3b..1463056b55b2 100644
--- a/eng/tools/spec-gen-sdk-runner/src/types.ts
+++ b/eng/tools/spec-gen-sdk-runner/src/types.ts
@@ -19,6 +19,14 @@ export interface SpecGenSdkCmdInput {
   headBranch?: string;
 }
 
+/**
+ * Data for the API view request.
+ */
+export interface APIViewRequestData {
+  packageName: string;
+  filePath: string;
+}
+
 /*
  * VsoLogs is a map of task names to log entries. Each log entry contains an array of errors and warnings.
  */
@@ -38,6 +46,7 @@ export interface SpecGenSdkArtifactInfo {
   result: string;
   labelAction?: boolean;
   isSpecGenSdkCheckRequired: boolean;
+  apiViewRequestData: APIViewRequestData [];
 }
 
 /**
diff --git a/eng/tools/spec-gen-sdk-runner/test/src/command-helper.test.ts b/eng/tools/spec-gen-sdk-runner/test/src/command-helper.test.ts
index 3c13fdb9d8b5..0a4e59b4ebbc 100644
--- a/eng/tools/spec-gen-sdk-runner/test/src/command-helper.test.ts
+++ b/eng/tools/spec-gen-sdk-runner/test/src/command-helper.test.ts
@@ -14,6 +14,7 @@ import {
   setPipelineVariables,
 } from "../../src/command-helpers.js";
 import { LogLevel } from "../../src/log.js";
+import { APIViewRequestData } from "../../src/types.js";
 
 // Get the absolute path to the repo root
 const currentFilePath = fileURLToPath(import.meta.url);
@@ -290,12 +291,16 @@ describe("commands.ts", () => {
       const mockBreakingchangeLabel = "breaking-change";
       const mockhasBreakingChange = false;
       const mockhasManagementPlaneSpecs = false;
+      const mockStagedArtifactsFolder = "mockStagedArtifactsFolder";
+      const mockApiViewRequestData: APIViewRequestData [] = [];
       const result = generateArtifact(
         mockCommandInput,
         mockResult,
         mockBreakingchangeLabel,
         mockhasBreakingChange,
         mockhasManagementPlaneSpecs,
+        mockStagedArtifactsFolder,
+        mockApiViewRequestData
       );
 
       const breakingChangeLabelArtifactPath = path.normalize(
@@ -314,6 +319,7 @@ describe("commands.ts", () => {
             result: "succeeded",
             labelAction: false,
             isSpecGenSdkCheckRequired: false,
+            apiViewRequestData: []
           },
           undefined,
           2,
@@ -327,6 +333,11 @@ describe("commands.ts", () => {
         "SpecGenSdkArtifactPath",
         "out/spec-gen-sdk-artifact",
       );
+
+      expect(log.setVsoVariable).toHaveBeenCalledWith(
+        "StagedArtifactsFolder",
+        "mockStagedArtifactsFolder",
+      );
       expect(log.setVsoVariable).toHaveBeenCalledWith("BreakingChangeLabelAction", "remove");
       expect(log.setVsoVariable).toHaveBeenCalledWith("BreakingChangeLabel", "breaking-change");
     });
@@ -358,12 +369,16 @@ describe("commands.ts", () => {
       const mockBreakingchangeLabel = "breaking-change";
       const mockhasBreakingChange = false;
       const mockhasManagementPlaneSpecs = false;
+      const mockStagedArtifactsFolder = "";
+      const mockApiViewRequestData: APIViewRequestData [] = [];
       const result = generateArtifact(
         mockCommandInput,
         mockResult,
         mockBreakingchangeLabel,
         mockhasBreakingChange,
         mockhasManagementPlaneSpecs,
+        mockStagedArtifactsFolder,
+        mockApiViewRequestData,
       );
 
       expect(result).toBe(1);
diff --git a/package-lock.json b/package-lock.json
index 6dd8226eb112..c0f3097364ac 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,7 +7,7 @@
       "name": "azure-rest-api-specs",
       "devDependencies": {
         "@autorest/openapi-to-typespec": "0.11.0",
-        "@azure-tools/spec-gen-sdk": "~0.4.2",
+        "@azure-tools/spec-gen-sdk": "~0.5.1",
         "@azure-tools/specs-shared": "file:.github/shared",
         "@azure-tools/typespec-apiview": "0.7.0",
         "@azure-tools/typespec-autorest": "0.56.0",
@@ -1881,9 +1881,9 @@
       "link": true
     },
     "node_modules/@azure-tools/spec-gen-sdk": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/@azure-tools/spec-gen-sdk/-/spec-gen-sdk-0.4.2.tgz",
-      "integrity": "sha512-ASNhxiAKe6BvY71CfcGV6dhdvWeZ/TtDwxiGSoWBiutH/ges0YUOlvl4ZQq1iwc2y102mPV0gHyj5GYlsFJ8DQ==",
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/@azure-tools/spec-gen-sdk/-/spec-gen-sdk-0.5.1.tgz",
+      "integrity": "sha512-A8tUduTYniMrXxgjCjYW8nojN287yFPmoDpLCHn5deE30Hes2BS61Kgu6VO+Lq0jgLfoQPUXACUDfWGLi3CG7w==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
diff --git a/package.json b/package.json
index 2a981e49def8..5ae4286d1022 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "azure-rest-api-specs",
   "devDependencies": {
-    "@azure-tools/spec-gen-sdk": "~0.4.2",
+    "@azure-tools/spec-gen-sdk": "~0.5.1",
     "@azure-tools/specs-shared": "file:.github/shared",
     "@azure-tools/typespec-apiview": "0.7.0",
     "@azure-tools/typespec-autorest": "0.56.0",


Reviews:
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T15:33:28Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T15:35:04Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T15:35:41Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T15:41:44Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T15:45:01Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T15:46:11Z
user: login: raych1
body: We need to validate this change for these matrixes:
- public spec repo
- private spec repo
- pull request trigger
- manual trigger
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-01T15:47:32Z
user: login: weshaggard
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T17:10:12Z
user: login: weshaggard
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T17:10:49Z
user: login: chidozieononiwu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T00:34:44Z
user: login: chidozieononiwu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T00:35:36Z
user: login: chidozieononiwu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T00:36:33Z
user: login: chidozieononiwu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T00:37:09Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T15:17:02Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T15:30:43Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T15:37:33Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T15:39:30Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T15:42:30Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T15:43:39Z
user: login: chidozieononiwu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T16:37:55Z
user: login: chidozieononiwu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T16:42:46Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T17:53:34Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T17:55:56Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T21:22:16Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T21:22:29Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-02T21:25:11Z
user: login: chidozieononiwu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T17:06:37Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T00:11:34Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T00:12:52Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T00:00:15Z
user: login: raych1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-08T02:48:12Z
user: login: raych1
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-09T00:33:49Z
user: login: raych1
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-09T21:48:02Z

Review comments:
diff_hunk: @@ -70,6 +70,10 @@ extends:
           - pwsh: |
               $tspConfigPathPattern = '^specification\/([^\/]+\/)+tspconfig\.yaml$'
               $readmePathPattern = '^specification\/([^\/]+\/){2,}readme\.md$'
+              if ('$(Build.Reason)' -eq 'PullRequest') {
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: This is not needed. $SpecRepoCommit is set to `$(Build.SourceVersion)` which is already the head commit of the spec PR in PR case. 
created_at: 2025-05-01T15:33:28Z
author_association: MEMBER
diff_hunk: @@ -197,16 +201,20 @@ extends:
                 $optional_params || true # return true always to suppress exit error logging
             displayName: 'Generate SDK'
 
-          - task: Powershell@2
-            inputs:
-              filePath: $(SdkRepoDirectory)/eng/common/scripts/Save-Package-Properties.ps1
-              arguments: >
-                -ServiceDirectory $(GeneratedSDK.ServiceName)
-                -OutDirectory $(GeneratedSDK.StagedArtifactsFolder)/PackageInfo
-              pwsh: true
-              workingDirectory: $(SdkRepoDirectory)
+          - pwsh: |
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: Is this only for debug purpose? Can you remove it when merge this PR? Or move to the next step (ApiView-Helpers.ps1) if you want to output them?
created_at: 2025-05-01T15:35:04Z
author_association: MEMBER
diff_hunk: @@ -197,16 +201,20 @@ extends:
                 $optional_params || true # return true always to suppress exit error logging
             displayName: 'Generate SDK'
 
-          - task: Powershell@2
-            inputs:
-              filePath: $(SdkRepoDirectory)/eng/common/scripts/Save-Package-Properties.ps1
-              arguments: >
-                -ServiceDirectory $(GeneratedSDK.ServiceName)
-                -OutDirectory $(GeneratedSDK.StagedArtifactsFolder)/PackageInfo
-              pwsh: true
-              workingDirectory: $(SdkRepoDirectory)
+          - pwsh: |
+              Write-Host "Service Names: $(SpecGenSdkServiceNames)"
+              Write-Host "Staged SDK Folder: $(SpecGenSdkArtifactPath)"
+            displayName: 'View SDK Generation Runtime Variables'
+
+          - pwsh: |
+              . $(SdkRepoDirectory)/eng/common/scripts/Helpers/ApiView-Helpers.ps1
+              Save-PackageProperties `
+                -repoRoot $(SdkRepoDirectory) `
+                -serviceNames $(SpecGenSdkServiceNames) `
+                -outPutDirectory $(SpecGenSdkArtifactPath)/PackageInfo
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: ```suggestion
                -outputDirectory $(SpecGenSdkArtifactPath)/PackageInfo
```
created_at: 2025-05-01T15:35:40Z
author_association: MEMBER
diff_hunk: @@ -17,6 +17,8 @@ export interface SpecGenSdkCmdInput {
   specRepoHttpsUrl: string;
   headRepoHttpsUrl?: string;
   headBranch?: string;
+  stagedArtifactsFolder?: string;
path: eng/tools/spec-gen-sdk-runner/src/types.ts
user: login: raych1
body: These two new properties are output info of `spec-gen-sdk` tool and we should not add them to this type. `SpecGenSdkCmdInput` is the input parameters for `spec-gen-sdk` tool. 
created_at: 2025-05-01T15:41:44Z
author_association: MEMBER
diff_hunk: @@ -130,6 +131,19 @@ export async function generateSdkForSpecPr(): Promise<number> {
       // Read the execution report to aggreate the generation results
       executionReport = getExecutionReport(commandInput);
       currentExecutionResult = executionReport.executionResult;
+      if (executionReport.stagedArtifactsFolder) {
+        commandInput.stagedArtifactsFolder = executionReport.stagedArtifactsFolder;
+      }
+      
+      for (const pkg of executionReport.packages) {
+        if (serviceNames.length > 0) {
+          serviceNames += ",";
+        }
+        if (pkg.serviceName) {
path: eng/tools/spec-gen-sdk-runner/src/commands.ts
user: login: raych1
body: when serviceName is empty, extra ',' will be added to serviceNames. We might move line#140 to within 'if' block then remove the ending ',' at last. 
created_at: 2025-05-01T15:45:00Z
author_association: MEMBER
diff_hunk: @@ -243,7 +243,7 @@ export function generateArtifact(
 ): number {
   const specGenSdkArtifactName = "spec-gen-sdk-artifact";
   const specGenSdkArtifactFileName = specGenSdkArtifactName + ".json";
-  const specGenSdkArtifactPath = "out/spec-gen-sdk-artifact";
+  const specGenSdkArtifactPath = commandInput.stagedArtifactsFolder!
path: eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
user: login: raych1
body: `out/spec-gen-sdk-artifact` is a specific artifact called 'spec-gen-sdk-artifact'. Don't change this and you could create another variable to keep 'stagedArtifactsFolder'.
created_at: 2025-05-01T15:46:11Z
author_association: MEMBER
diff_hunk: @@ -197,16 +201,20 @@ extends:
                 $optional_params || true # return true always to suppress exit error logging
             displayName: 'Generate SDK'
 
-          - task: Powershell@2
-            inputs:
-              filePath: $(SdkRepoDirectory)/eng/common/scripts/Save-Package-Properties.ps1
-              arguments: >
-                -ServiceDirectory $(GeneratedSDK.ServiceName)
-                -OutDirectory $(GeneratedSDK.StagedArtifactsFolder)/PackageInfo
-              pwsh: true
-              workingDirectory: $(SdkRepoDirectory)
+          - pwsh: |
+              Write-Host "Service Names: $(SpecGenSdkServiceNames)"
+              Write-Host "Staged SDK Folder: $(SpecGenSdkArtifactPath)"
+            displayName: 'View SDK Generation Runtime Variables'
+
+          - pwsh: |
+              . $(SdkRepoDirectory)/eng/common/scripts/Helpers/ApiView-Helpers.ps1
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: weshaggard
body: Looking at the Save-PackageProperties helper it looks to just call the save packages in a loop one for each service. Is there a reason you did that as a helper function as opposed to just doing it inline here?
created_at: 2025-05-01T17:10:12Z
author_association: MEMBER
diff_hunk: @@ -264,6 +264,7 @@ export function generateArtifact(
       path.join(commandInput.workingFolder, specGenSdkArtifactPath, specGenSdkArtifactFileName),
       JSON.stringify(artifactInfo, undefined, 2),
     );
+    setVsoVariable("SpecGenSdkServiceNames", commandInput.serviceNames!);
path: eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
user: login: weshaggard
body: What does the `!` at the end represent?
created_at: 2025-05-01T17:10:49Z
author_association: MEMBER
diff_hunk: @@ -70,6 +70,10 @@ extends:
           - pwsh: |
               $tspConfigPathPattern = '^specification\/([^\/]+\/)+tspconfig\.yaml$'
               $readmePathPattern = '^specification\/([^\/]+\/){2,}readme\.md$'
+              if ('$(Build.Reason)' -eq 'PullRequest') {
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: chidozieononiwu
body: We no longer use $(Build.SourceVersion) for APIView
created_at: 2025-05-02T00:34:44Z
author_association: MEMBER
diff_hunk: @@ -243,7 +243,7 @@ export function generateArtifact(
 ): number {
   const specGenSdkArtifactName = "spec-gen-sdk-artifact";
   const specGenSdkArtifactFileName = specGenSdkArtifactName + ".json";
-  const specGenSdkArtifactPath = "out/spec-gen-sdk-artifact";
+  const specGenSdkArtifactPath = commandInput.stagedArtifactsFolder!
path: eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
user: login: chidozieononiwu
body: Right, I am changing this
created_at: 2025-05-02T00:35:36Z
author_association: MEMBER
diff_hunk: @@ -197,16 +201,20 @@ extends:
                 $optional_params || true # return true always to suppress exit error logging
             displayName: 'Generate SDK'
 
-          - task: Powershell@2
-            inputs:
-              filePath: $(SdkRepoDirectory)/eng/common/scripts/Save-Package-Properties.ps1
-              arguments: >
-                -ServiceDirectory $(GeneratedSDK.ServiceName)
-                -OutDirectory $(GeneratedSDK.StagedArtifactsFolder)/PackageInfo
-              pwsh: true
-              workingDirectory: $(SdkRepoDirectory)
+          - pwsh: |
+              Write-Host "Service Names: $(SpecGenSdkServiceNames)"
+              Write-Host "Staged SDK Folder: $(SpecGenSdkArtifactPath)"
+            displayName: 'View SDK Generation Runtime Variables'
+
+          - pwsh: |
+              . $(SdkRepoDirectory)/eng/common/scripts/Helpers/ApiView-Helpers.ps1
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: chidozieononiwu
body: The service names are created at run time so I cannot do the for loop with them here. Either way am moving away from this approach
created_at: 2025-05-02T00:36:33Z
author_association: MEMBER
diff_hunk: @@ -264,6 +264,7 @@ export function generateArtifact(
       path.join(commandInput.workingFolder, specGenSdkArtifactPath, specGenSdkArtifactFileName),
       JSON.stringify(artifactInfo, undefined, 2),
     );
+    setVsoVariable("SpecGenSdkServiceNames", commandInput.serviceNames!);
path: eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
user: login: chidozieononiwu
body: Assigning `string | undefined` to `string`.
created_at: 2025-05-02T00:37:08Z
author_association: MEMBER
diff_hunk: @@ -70,6 +70,10 @@ extends:
           - pwsh: |
               $tspConfigPathPattern = '^specification\/([^\/]+\/)+tspconfig\.yaml$'
               $readmePathPattern = '^specification\/([^\/]+\/){2,}readme\.md$'
+              if ('$(Build.Reason)' -eq 'PullRequest') {
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: Can you remove this if it's no longer used?
created_at: 2025-05-02T15:17:02Z
author_association: MEMBER
diff_hunk: @@ -19,6 +19,15 @@ export interface SpecGenSdkCmdInput {
   headBranch?: string;
 }
 
+/**
+ * Data for the API view request.
+ */
+export interface APIViewRequestData {
+  packageName: string;
+  filePath: string;
+  language: string;
path: eng/tools/spec-gen-sdk-runner/src/types.ts
user: login: raych1
body: Language will always be the same in a pipeline run. Can you use `SpecGenSdkArtifactInfo.language`? In addition, language here is like this format: 'azure-sdk-for-python'. I think you need to extract the 'python' from this value when using it in API view script.
created_at: 2025-05-02T15:30:42Z
author_association: MEMBER
diff_hunk: @@ -280,8 +273,13 @@ extends:
                     -OpenAsDraft $true
 
           - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
-            - template: /eng/common/pipelines/templates/steps/detect-api-changes.yml
-              parameters:
-                ArtifactPath: $(GeneratedSDK.StagedArtifactsFolder)
-                ArtifactName: $(PipelineArtifactsName)
-                RepoRoot: $(SdkRepoDirectory)
+            - pwsh: |
+                . $(SpecRepoDirectory)/eng/common/scripts/Helpers/ApiView-Helpers.ps1
+                Create-API-Review `
+                  -specGenSDKArtifactPath "$(System.DefaultWorkingDirectory)/$(SpecGenSdkArtifactPath)/$(SpecGenSdkArtifactPath).json" `
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: ```suggestion
                  -specGenSDKArtifactPath "$(System.DefaultWorkingDirectory)/$(SpecGenSdkArtifactPath)/$(SpecGenSdkArtifactName).json" `
```
created_at: 2025-05-02T15:37:33Z
author_association: MEMBER
diff_hunk: @@ -86,6 +86,8 @@ export async function generateSdkForSpecPr(): Promise<number> {
   let currentRunHasBreakingChange = false;
   let overallExecutionResult = "";
   let currentExecutionResult = "";
+  let stagedArtifactsFolder : string | undefined;
path: eng/tools/spec-gen-sdk-runner/src/commands.ts
user: login: raych1
body: Can you just initialize `stagedArtifactsFolder` as "" as there is empty string check when use it in the pipeline?
created_at: 2025-05-02T15:39:30Z
author_association: MEMBER
diff_hunk: @@ -238,3 +238,44 @@ function Save-PackageProperties ($repoRoot, $serviceNames, $outputDirectory) {
     & $scriptPath -ServiceDirectory $serviceDirectory.Trim() -OutDirectory $outputDirectory
   }
 }
+
+function Create-API-Review {
+  param (
+    [string]$apiviewEndpoint = "https://apiview.dev/PullRequest/DetectAPIChanges",
+    [string]$specGenSDKArtifactPath,
+    [string]$apiviewArtifactName,
+    [string]$buildId,
+    [string]$commitish,
+    [string]$repoName,
+    [string]$pullRequestNumber
+  )
+  . ${PSScriptRoot}\..\logging.ps1
+  $specGenSDKContent = Get-Content -Path $SpecGenSDKArtifactPath -Raw | ConvertFrom-Json
+  $uri = [System.UriBuilder]$apiviewEndpoint
+  $query = [System.Web.HttpUtility]::ParseQueryString('')
+  $query.Add('artifactName', $apiviewArtifactName)
+  $query.Add('buildId', $buildId)
+  $query.Add('commitSha', $commitish)
+  $query.Add('repoName', $repoName)
+  $query.Add('pullRequestNumber', $pullRequestNumber)
+  
+  foreach ($requestData in $specGenSDKContent.apiViewRequestData) {
+    $requestUri = $uri
+    $requestParam = $query
+    $requestParam.Add('packageName', $requestData.packageName)
+    $requestParam.Add('filePath', $requestData.filePath)
+    $requestParam.Add('language', $requestData.language)
+    $requestUri.query = $requestParam.toString()
+    LogInfo "Request URI: $($requestUri.Uri.OriginalString)"
+
+    try
+    {
+      $response = Invoke-WebRequest -Method 'GET' -Uri $uri.Uri -MaximumRetryCount 3
path: eng/common/scripts/Helpers/ApiView-Helpers.ps1
user: login: raych1
body: We will need to report the errors and details for based on response codes.
created_at: 2025-05-02T15:42:30Z
author_association: MEMBER
diff_hunk: @@ -238,3 +238,44 @@ function Save-PackageProperties ($repoRoot, $serviceNames, $outputDirectory) {
     & $scriptPath -ServiceDirectory $serviceDirectory.Trim() -OutDirectory $outputDirectory
   }
 }
+
+function Create-API-Review {
+  param (
+    [string]$apiviewEndpoint = "https://apiview.dev/PullRequest/DetectAPIChanges",
+    [string]$specGenSDKArtifactPath,
+    [string]$apiviewArtifactName,
+    [string]$buildId,
+    [string]$commitish,
+    [string]$repoName,
+    [string]$pullRequestNumber
+  )
+  . ${PSScriptRoot}\..\logging.ps1
+  $specGenSDKContent = Get-Content -Path $SpecGenSDKArtifactPath -Raw | ConvertFrom-Json
+  $uri = [System.UriBuilder]$apiviewEndpoint
+  $query = [System.Web.HttpUtility]::ParseQueryString('')
+  $query.Add('artifactName', $apiviewArtifactName)
+  $query.Add('buildId', $buildId)
+  $query.Add('commitSha', $commitish)
+  $query.Add('repoName', $repoName)
+  $query.Add('pullRequestNumber', $pullRequestNumber)
+  
+  foreach ($requestData in $specGenSDKContent.apiViewRequestData) {
+    $requestUri = $uri
+    $requestParam = $query
+    $requestParam.Add('packageName', $requestData.packageName)
+    $requestParam.Add('filePath', $requestData.filePath)
path: eng/common/scripts/Helpers/ApiView-Helpers.ps1
user: login: raych1
body: Does python need to use `codeFile` instead of `filePath`?
created_at: 2025-05-02T15:43:39Z
author_association: MEMBER
diff_hunk: @@ -70,6 +70,10 @@ extends:
           - pwsh: |
               $tspConfigPathPattern = '^specification\/([^\/]+\/)+tspconfig\.yaml$'
               $readmePathPattern = '^specification\/([^\/]+\/){2,}readme\.md$'
+              if ('$(Build.Reason)' -eq 'PullRequest') {
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: chidozieononiwu
body: We need to use `$(System.PullRequest.SourceCommitId)` for creating PR apiview
created_at: 2025-05-02T16:37:55Z
author_association: MEMBER
diff_hunk: @@ -19,6 +19,15 @@ export interface SpecGenSdkCmdInput {
   headBranch?: string;
 }
 
+/**
+ * Data for the API view request.
+ */
+export interface APIViewRequestData {
+  packageName: string;
+  filePath: string;
+  language: string;
path: eng/tools/spec-gen-sdk-runner/src/types.ts
user: login: chidozieononiwu
body: The correct language is in genertedOutput maybe I can just use that.
created_at: 2025-05-02T16:42:46Z
author_association: MEMBER
diff_hunk: @@ -70,6 +70,10 @@ extends:
           - pwsh: |
               $tspConfigPathPattern = '^specification\/([^\/]+\/)+tspconfig\.yaml$'
               $readmePathPattern = '^specification\/([^\/]+\/){2,}readme\.md$'
+              if ('$(Build.Reason)' -eq 'PullRequest') {
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: Ok. You need to move this assignment to step of `Generate SDK` as this value cannot be changed before the `sparse checkout` step.
created_at: 2025-05-02T17:53:34Z
author_association: MEMBER
diff_hunk: @@ -19,6 +19,15 @@ export interface SpecGenSdkCmdInput {
   headBranch?: string;
 }
 
+/**
+ * Data for the API view request.
+ */
+export interface APIViewRequestData {
+  packageName: string;
+  filePath: string;
+  language: string;
path: eng/tools/spec-gen-sdk-runner/src/types.ts
user: login: raych1
body: It's not necessary to access `generatedOutput`. Instead, extract the short language name from the string of `SpecGenSdkArtifactInfo.language`.
created_at: 2025-05-02T17:55:56Z
author_association: MEMBER
diff_hunk: @@ -240,6 +241,8 @@ export function generateArtifact(
   breakingChangeLabel: string,
   hasBreakingChange: boolean,
   hasManagementPlaneSpecs: boolean,
+  stagedArtifactsFolder: string | undefined,
path: eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
user: login: raych1
body: ```suggestion
  stagedArtifactsFolder: string,
```
created_at: 2025-05-02T21:22:16Z
author_association: MEMBER
diff_hunk: @@ -259,13 +262,15 @@ export function generateArtifact(
       labelAction: hasBreakingChange,
       isSpecGenSdkCheckRequired:
         hasManagementPlaneSpecs && SpecGenSdkRequiredSettings[commandInput.sdkLanguage as SdkName],
+      apiViewRequestData: apiViewRequestData
     };
     fs.writeFileSync(
       path.join(commandInput.workingFolder, specGenSdkArtifactPath, specGenSdkArtifactFileName),
       JSON.stringify(artifactInfo, undefined, 2),
     );
     setVsoVariable("SpecGenSdkArtifactName", specGenSdkArtifactName);
     setVsoVariable("SpecGenSdkArtifactPath", specGenSdkArtifactPath);
+    setVsoVariable("StagedArtifactsFolder", stagedArtifactsFolder!);
path: eng/tools/spec-gen-sdk-runner/src/command-helpers.ts
user: login: raych1
body: ```suggestion
    setVsoVariable("StagedArtifactsFolder", stagedArtifactsFolder);
```
created_at: 2025-05-02T21:22:29Z
author_association: MEMBER
diff_hunk: @@ -238,3 +238,50 @@ function Save-PackageProperties ($repoRoot, $serviceNames, $outputDirectory) {
     & $scriptPath -ServiceDirectory $serviceDirectory.Trim() -OutDirectory $outputDirectory
   }
 }
+
+function Create-API-Review {
+  param (
+    [string]$apiviewEndpoint = "https://apiview.dev/PullRequest/DetectAPIChanges",
+    [string]$specGenSDKArtifactPath,
+    [string]$apiviewArtifactName,
+    [string]$buildId,
+    [string]$commitish,
+    [string]$repoName,
+    [string]$pullRequestNumber
+  )
+  . ${PSScriptRoot}\..\logging.ps1
+  $specGenSDKContent = Get-Content -Path $SpecGenSDKArtifactPath -Raw | ConvertFrom-Json
+  $language = ($specGenSDKContent.language -split "-")[-1]
+  
+  foreach ($requestData in $specGenSDKContent.apiViewRequestData) {
+    $requestUri = [System.UriBuilder]$apiviewEndpoint
+    $requestParam = [System.Web.HttpUtility]::ParseQueryString('')
+    $requestParam.Add('artifactName', $apiviewArtifactName)
+    $requestParam.Add('buildId', $buildId)
+    $requestParam.Add('commitSha', $commitish)
+    $requestParam.Add('repoName', $repoName)
+    $requestParam.Add('pullRequestNumber', $pullRequestNumber)
+    $requestParam.Add('packageName', $requestData.packageName)
+    $requestParam.Add('filePath', $requestData.filePath)
+    $requestParam.Add('language', $language)
+    $requestUri.query = $requestParam.toString()
+    LogInfo "Request URI: $($requestUri.Uri.OriginalString)"
+
+    try
+    {
+      $response = Invoke-WebRequest -Method 'GET' -Uri $requestUri.Uri -MaximumRetryCount 3
+      if ($response.StatusCode -gt 200) {
+        LogSuccess "API review request created successfully.`n$($response.StatusCode)`n$($response.Content)"
+      }
+      else {
path: eng/common/scripts/Helpers/ApiView-Helpers.ps1
user: login: raych1
body: Can you look at [current code](https://devdiv.visualstudio.com/DevDiv/_git/openapi-alps?path=/public/publish-pipeline-result/src/ApiViewClient.ts&version=GBmain&line=105&lineEnd=106&lineStartColumn=1&lineEndColumn=1&lineStyle=plain&_a=contents) if they need to be the same here? We need to keep the same experience for the PR authors.
created_at: 2025-05-02T21:25:10Z
author_association: MEMBER
diff_hunk: @@ -70,6 +70,10 @@ extends:
           - pwsh: |
               $tspConfigPathPattern = '^specification\/([^\/]+\/)+tspconfig\.yaml$'
               $readmePathPattern = '^specification\/([^\/]+\/){2,}readme\.md$'
+              if ('$(Build.Reason)' -eq 'PullRequest') {
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: chidozieononiwu
body: Why can this not be changed before the sparse checkout step?

created_at: 2025-05-06T17:06:37Z
author_association: MEMBER
diff_hunk: @@ -70,6 +70,10 @@ extends:
           - pwsh: |
               $tspConfigPathPattern = '^specification\/([^\/]+\/)+tspconfig\.yaml$'
               $readmePathPattern = '^specification\/([^\/]+\/){2,}readme\.md$'
+              if ('$(Build.Reason)' -eq 'PullRequest') {
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: We need to check out against the merge commit in PR scenario instead of the head commit in the PR branch.
created_at: 2025-05-07T00:11:33Z
author_association: MEMBER
diff_hunk: @@ -130,6 +132,19 @@ export async function generateSdkForSpecPr(): Promise<number> {
       // Read the execution report to aggreate the generation results
       executionReport = getExecutionReport(commandInput);
       currentExecutionResult = executionReport.executionResult;
+
+      if (executionReport.stagedArtifactsFolder) {
+        stagedArtifactsFolder = executionReport.stagedArtifactsFolder;
+      }
+      const apiViewArtifactRelPath = path.relative(executionReport.stagedArtifactsFolder, executionReport.sdkApiViewArtifactFolder);
+      for (const pkg of executionReport.packages) {
+        const fileName = path.basename(pkg.apiViewArtifact);
path: eng/tools/spec-gen-sdk-runner/src/commands.ts
user: login: raych1
body: Can you add null check for `pkg.apiViewArtifact` as it could be null?
created_at: 2025-05-07T00:12:52Z
author_association: MEMBER
diff_hunk: @@ -130,6 +132,21 @@ export async function generateSdkForSpecPr(): Promise<number> {
       // Read the execution report to aggreate the generation results
       executionReport = getExecutionReport(commandInput);
       currentExecutionResult = executionReport.executionResult;
+
+      if (executionReport.stagedArtifactsFolder) {
+        stagedArtifactsFolder = executionReport.stagedArtifactsFolder;
+      }
+      const apiViewArtifactRelPath = path.relative(executionReport.stagedArtifactsFolder, executionReport.sdkApiViewArtifactFolder);
path: eng/tools/spec-gen-sdk-runner/src/commands.ts
user: login: raych1
body: `executionReport.sdkApiViewArtifactFolder)` is an optional property, we will need to do null check.
created_at: 2025-05-08T00:00:15Z
author_association: MEMBER
diff_hunk: @@ -279,9 +268,14 @@ extends:
                     -PRBody "$(GeneratedSDKInformation)"
                     -OpenAsDraft $true
 
-            - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
-              - template: /eng/common/pipelines/templates/steps/detect-api-changes.yml
-                parameters:
-                  ArtifactPath: $(GeneratedSDK.StagedArtifactsFolder)
-                  ArtifactName: $(PipelineArtifactsName)
-                  RepoRoot: $(SdkRepoDirectory)
+          - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
+            - pwsh: |
+                . $(SpecRepoDirectory)/eng/common/scripts/Helpers/ApiView-Helpers.ps1
+                Create-API-Review `
+                  -specGenSDKArtifactPath "$(System.DefaultWorkingDirectory)/$(SpecGenSdkArtifactPath)/$(SpecGenSdkArtifactName).json" `
path: eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
user: login: raych1
body: Both `SpecGenSdkArtifactPath` variable and `SpecGenSdkArtifactName` variable might not be set in the previous steps. It needs the existence check in the `helpers.ps1` file.
created_at: 2025-05-08T02:48:12Z
author_association: MEMBER

PR comments:
user: login: openapi-pipeline-app[bot]
created_at: 2025-05-01T01:26:42Z
body: <h2>Next Steps to Merge</h2>Next steps that must be taken to merge this PR: <br/><ul><li>❌ The required check named <code>Protected Files</code> has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the <a href="https://aka.ms/ci-fix">aka.ms/ci-fix</a> guide</li></ul>
author_association: NONE
user: login: openapi-pipeline-app[bot]
created_at: 2025-05-01T01:26:43Z
body: PR validation pipeline restarted successfully. If there is ApiView generated, it will be updated in this comment.
author_association: NONE
user: login: openapi-pipeline-app[bot]
created_at: 2025-05-02T22:16:03Z
body: PR validation pipeline can not start as the pull request is not merged or mergeable - most likely it has merge conflicts.
author_association: NONE