diff --git a/.github/workflows/sdk-breaking-change-labels.yaml b/.github/workflows/sdk-breaking-change-labels.yaml index ec69a2f9d4e6..013a43ee8dab 100644 --- a/.github/workflows/sdk-breaking-change-labels.yaml +++ b/.github/workflows/sdk-breaking-change-labels.yaml @@ -6,14 +6,15 @@ on: permissions: contents: read + pull-requests: read + id-token: write jobs: sdk-breaking-change-labels: - # Only run this job when the check run is from Azure Pipelines SDK Generation job in the azure-sdk/public(id: 29ec6040-b234-4e31-b139-33dc4287b756) project + # Only run this job when the check run is 'SDK Validation *' if: | github.event.check_run.check_suite.app.name == 'Azure Pipelines' && - contains(github.event.check_run.name, 'SDK Validation') && - endsWith(github.event.check_run.external_id, '29ec6040-b234-4e31-b139-33dc4287b756') + contains(github.event.check_run.name, 'SDK Validation') name: SDK Breaking Change Labels runs-on: ubuntu-24.04 steps: @@ -21,7 +22,23 @@ jobs: with: sparse-checkout: | .github - + + # Only run the login and get token steps when the respository is azure-rest-api-specs-pr + - if: github.event.repository.name == 'azure-rest-api-specs-pr' + name: Azure Login with Workload Identity Federation + uses: azure/login@v2 + with: + client-id: "936c56f0-298b-467f-b702-3ad5bf4b15c1" + tenant-id: "72f988bf-86f1-41af-91ab-2d7cd011db47" + allow-no-subscriptions: true + + - if: github.event.repository.name == 'azure-rest-api-specs-pr' + name: Get ADO Token via Managed Identity + run: | + # Get token for Azure DevOps resource + ADO_TOKEN=$(az account get-access-token --resource "499b84ac-1321-427f-aa17-267ca6975798" --query "accessToken" -o tsv) + echo "ADO_TOKEN=$ADO_TOKEN" >> $GITHUB_ENV + - name: Get label and action id: get-label-and-action uses: actions/github-script@v7 @@ -42,8 +59,9 @@ jobs: value: "${{ fromJson(steps.get-label-and-action.outputs.result).labelAction == 'add' }}" - if: | - (fromJson(steps.get-label-and-action.outputs.result).labelAction == 'add' || - fromJson(steps.get-label-and-action.outputs.result).labelAction == 'remove') + ((fromJson(steps.get-label-and-action.outputs.result).labelAction == 'add' || + fromJson(steps.get-label-and-action.outputs.result).labelAction == 'remove') && + fromJson(steps.get-label-and-action.outputs.result).issueNumber > 0) name: Upload artifact with issue number uses: ./.github/actions/add-empty-artifact with: diff --git a/.github/workflows/src/artifacts.js b/.github/workflows/src/artifacts.js index b690a963b678..97c678c6c675 100644 --- a/.github/workflows/src/artifacts.js +++ b/.github/workflows/src/artifacts.js @@ -188,7 +188,14 @@ export async function fetchFailedArtifact({ .filter((artifact) => artifact.name.includes(artifactName)) .sort((a, b) => b.name.localeCompare(a.name)); // Descending order (Z to A) if (artifactsList.length === 0) { - throw new Error(`No artifacts found with name containing ${artifactName}`); + const message = `No artifacts found with name containing ${artifactName}`; + core.warning(message); + // Return a Response-like object using the global Response constructor + return new Response(message, { + status: 404, + statusText: message, + headers: { "Content-Type": "text/plain" }, + }); } artifactName = artifactsList[0].name; apiUrl = `${ado_project_url}/_apis/build/builds/${ado_build_id}/artifacts?artifactName=${artifactName}&api-version=7.0`; diff --git a/.github/workflows/src/context.js b/.github/workflows/src/context.js index 0f8ecf424c73..75314d1426db 100644 --- a/.github/workflows/src/context.js +++ b/.github/workflows/src/context.js @@ -10,7 +10,7 @@ import { getIssueNumber } from "./issues.js"; * @param {import('github-script').AsyncFunctionArguments['github']} github * @param {import('github-script').AsyncFunctionArguments['context']} context * @param {import('github-script').AsyncFunctionArguments['core']} core - * @returns {Promise<{owner: string, repo: string, head_sha: string, issue_number: number, run_id: number, ado_project_url?: string, ado_build_id?: string }>} + * @returns {Promise<{owner: string, repo: string, head_sha: string, issue_number: number, run_id: number, details_url?: string }>} */ export async function extractInputs(github, context, core) { core.info("extractInputs()"); @@ -24,7 +24,7 @@ export async function extractInputs(github, context, core) { // with debug enabled to replay the previous context. core.isDebug() && core.debug(`context: ${JSON.stringify(context)}`); - /** @type {{ owner: string, repo: string, head_sha: string, issue_number: number, run_id: number, ado_project_url?: string, ado_build_id?: string }} */ + /** @type {{ owner: string, repo: string, head_sha: string, issue_number: number, run_id: number, details_url?: string }} */ let inputs; // Add support for more event types as needed @@ -240,16 +240,6 @@ export async function extractInputs(github, context, core) { }; } else if (context.eventName === "check_run") { let checkRun = context.payload.check_run; - - // Extract the ADO build ID and project URL from the check run details URL - const buildUrlRegex = /^(.*?)(?=\/_build\/).*?[?&]buildId=(\d+)/; - const match = checkRun.details_url.match(buildUrlRegex); - if (!match) { - throw new Error( - `Could not extract build ID or project URL from check run details URL: ${checkRun.details_url}`, - ); - } - const payload = /** @type {import("@octokit/webhooks-types").CheckRunEvent} */ ( context.payload @@ -259,8 +249,7 @@ export async function extractInputs(github, context, core) { owner: repositoryInfo.owner, repo: repositoryInfo.repo, head_sha: checkRun.head_sha, - ado_build_id: match[2], - ado_project_url: match[1], + details_url: checkRun.details_url, issue_number: NaN, run_id: NaN, }; diff --git a/.github/workflows/src/sdk-breaking-change-labels.js b/.github/workflows/src/sdk-breaking-change-labels.js index 4ffe92d52f1f..a5e1ac4694e2 100644 --- a/.github/workflows/src/sdk-breaking-change-labels.js +++ b/.github/workflows/src/sdk-breaking-change-labels.js @@ -1,9 +1,11 @@ // @ts-check import { sdkLabels } from "../../shared/src/sdk-types.js"; +import { + getAdoBuildInfoFromUrl, + getAzurePipelineArtifact, +} from "./artifacts.js"; import { extractInputs } from "./context.js"; -import { getIssueNumber } from "./issues.js"; import { LabelAction } from "./label.js"; -import { fetchWithRetry } from "./retries.js"; /** * @typedef {import("../../shared/src/sdk-types.js").SdkName} SdkName @@ -25,39 +27,28 @@ import { fetchWithRetry } from "./retries.js"; */ export async function getLabelAndAction({ github, context, core }) { const inputs = await extractInputs(github, context, core); - const ado_build_id = inputs.ado_build_id; - const ado_project_url = inputs.ado_project_url; - const head_sha = inputs.head_sha; - if (!ado_build_id || !ado_project_url || !head_sha) { + const details_url = inputs.details_url; + if (!details_url) { throw new Error( - `Required inputs are not valid: ado_build_id:${ado_build_id}, ado_project_url:${ado_project_url}, head_sha:${head_sha}`, + `Required inputs are not valid: details_url:${details_url}`, ); } return await getLabelAndActionImpl({ - ado_build_id, - ado_project_url, - head_sha, + details_url, core, - github, }); } /** * @param {Object} params - * @param {string} params.ado_build_id - * @param {string} params.ado_project_url - * @param {string} params.head_sha + * @param {string} params.details_url * @param {typeof import("@actions/core")} params.core - * @param {(import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api)} params.github * @param {import('./retries.js').RetryOptions} [params.retryOptions] * @returns {Promise<{labelName: string | undefined, labelAction: LabelAction, issueNumber: number}>} */ export async function getLabelAndActionImpl({ - ado_build_id, - ado_project_url, - head_sha, + details_url, core, - github, retryOptions = {}, }) { // Override default logger from console.log to core.info @@ -67,67 +58,40 @@ export async function getLabelAndActionImpl({ let labelAction; /** @type {String | undefined} */ let labelName = ""; + const buildInfo = getAdoBuildInfoFromUrl(details_url); + const ado_project_url = buildInfo.projectUrl; + const ado_build_id = buildInfo.buildId; const artifactName = "spec-gen-sdk-artifact"; const artifactFileName = artifactName + ".json"; - const apiUrl = `${ado_project_url}/_apis/build/builds/${ado_build_id}/artifacts?artifactName=${artifactName}&api-version=7.0`; - core.info(`Calling Azure DevOps API to get the artifact: ${apiUrl}`); - - // Use Node.js fetch with retry to call the API - const response = await fetchWithRetry( - apiUrl, - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }, + const result = await getAzurePipelineArtifact({ + ado_build_id, + ado_project_url, + artifactName, + artifactFileName, + core, retryOptions, - ); - - if (response.status === 404) { - core.info( - `Artifact '${artifactName}' not found (404). This might be expected if there are no breaking changes.`, - ); - } else if (response.ok) { - // Step 1: Get the download URL for the artifact - /** @type {Artifacts} */ - const artifacts = /** @type {Artifacts} */ (await response.json()); - core.info(`Artifacts found: ${JSON.stringify(artifacts)}`); - if (!artifacts.resource || !artifacts.resource.downloadUrl) { - throw new Error( - `Download URL not found for the artifact ${artifactName}`, - ); - } - - let downloadUrl = artifacts.resource.downloadUrl; - const index = downloadUrl.indexOf("?format=zip"); - if (index !== -1) { - // Keep everything up to (but not including) "?format=zip" - downloadUrl = downloadUrl.substring(0, index); - } - downloadUrl += `?format=file&subPath=/${artifactFileName}`; - core.info(`Downloading artifact from: ${downloadUrl}`); - - // Step 2: Fetch Artifact Content (as a Buffer) with retry - const artifactResponse = await fetchWithRetry( - downloadUrl, - {}, - { logger: core.info }, + fallbackToFailedArtifact: true, + token: process.env.ADO_TOKEN, + }); + // Parse the JSON data + if (!result.artifactData) { + core.warning( + `Artifact '${artifactName}' not found in the build with details_url:${details_url} or failed to download it.`, ); - if (!artifactResponse.ok) { - throw new Error( - `Failed to fetch artifact: ${artifactResponse.statusText}`, + } else { + core.info(`Artifact content: ${result.artifactData}`); + // Parse the JSON data + const specGenSdkArtifactInfo = JSON.parse(result.artifactData); + const labelActionText = specGenSdkArtifactInfo.labelAction; + issue_number = parseInt(specGenSdkArtifactInfo.prNumber, 10); + if (!issue_number) { + core.warning( + `No PR number found in the artifact '${artifactName}' with details_url:${details_url}.`, ); } - const artifactData = await artifactResponse.text(); - core.info(`Artifact content: ${artifactData}`); - - // Parse the JSON data - const breakingChangeResult = JSON.parse(artifactData); - const labelActionText = breakingChangeResult.labelAction; /** @type {SdkName} */ - const breakingChangeLanguage = breakingChangeResult.language; + const breakingChangeLanguage = specGenSdkArtifactInfo.language; if (breakingChangeLanguage) { labelName = sdkLabels[`${breakingChangeLanguage}`].breakingChange; } @@ -138,19 +102,8 @@ export async function getLabelAndActionImpl({ } else if (labelActionText === false) { labelAction = LabelAction.Remove; } - - // Get the issue number from the check run - if (!issue_number) { - const { issueNumber } = await getIssueNumber({ head_sha, core, github }); - issue_number = issueNumber; - } - } else { - core.error( - `Failed to fetch artifacts: ${response.status}, ${response.statusText}`, - ); - const errorText = await response.text(); - core.error(`Error details: ${errorText}`); } + if (!labelAction) { core.info("No label action found, defaulting to None"); labelAction = LabelAction.None; diff --git a/.github/workflows/src/spec-gen-sdk-status.js b/.github/workflows/src/spec-gen-sdk-status.js index c09dd86d8146..34cffbeb155d 100644 --- a/.github/workflows/src/spec-gen-sdk-status.js +++ b/.github/workflows/src/spec-gen-sdk-status.js @@ -17,12 +17,11 @@ import { */ export default async function setSpecGenSdkStatus({ github, context, core }) { const inputs = await extractInputs(github, context, core); - const ado_build_id = inputs.ado_build_id; - const ado_project_url = inputs.ado_project_url; const head_sha = inputs.head_sha; - if (!ado_build_id || !ado_project_url || !head_sha) { + const details_url = inputs.details_url; + if (!details_url || !head_sha) { throw new Error( - `Required inputs are not valid: ado_build_id:${ado_build_id}, ado_project_url:${ado_project_url}, head_sha:${head_sha}`, + `Required inputs are not valid: details_url:${details_url}, head_sha:${head_sha}`, ); } const owner = inputs.owner; diff --git a/.github/workflows/test/artifacts.test.js b/.github/workflows/test/artifacts.test.js index 1c6f51e6bc36..489596e06626 100644 --- a/.github/workflows/test/artifacts.test.js +++ b/.github/workflows/test/artifacts.test.js @@ -703,7 +703,7 @@ describe("fetchFailedArtifact function", () => { expect(response).toBe(mockFetchResponse); }); - it("should throw an error when no matching artifacts are found", async () => { + it("should return 404 when no matching artifacts are found", async () => { // Mock response with no matching artifacts const mockListResponse = { ok: true, @@ -725,8 +725,11 @@ describe("fetchFailedArtifact function", () => { global.fetch.mockResolvedValue(mockListResponse); - // Call the function and expect it to throw - await expect(fetchFailedArtifact(defaultParams)).rejects.toThrow( + // Call the function and expect it to return a 404 response + const response = await fetchFailedArtifact(defaultParams); + expect(response.ok).toBe(false); + expect(response.status).toBe(404); + expect(response.statusText).toBe( `No artifacts found with name containing ${defaultParams.artifactName}`, ); }); diff --git a/.github/workflows/test/context.test.js b/.github/workflows/test/context.test.js index 114ad4f61c1b..6dbf0e9599f2 100644 --- a/.github/workflows/test/context.test.js +++ b/.github/workflows/test/context.test.js @@ -469,8 +469,8 @@ describe("extractInputs", () => { issue_number: NaN, head_sha: "abc123", run_id: NaN, - ado_build_id: "56789", - ado_project_url: "https://dev.azure.com/abc/123-456", + details_url: + "https://dev.azure.com/abc/123-456/_build/results?buildId=56789", }); }); @@ -481,11 +481,11 @@ describe("extractInputs", () => { payload: { action: "completed", check_run: { - details_url: "https://debc/123-456/_build/result/buildId=56789", + details_url: + "https://dev.azure.com/abc/123-456/_build/results?buildId=56789", head_sha: "abc123", }, repository: { - name: "TestRepoName", owner: { login: "TestRepoOwnerLogin", }, @@ -493,14 +493,6 @@ describe("extractInputs", () => { }, }; - await expect( - extractInputs(github, context, createMockCore()), - ).rejects.toThrow("from check run details URL"); - - context.payload.check_run.details_url = - "https://dev.azure.com/abc/123-456/_build/results?buildId=56789"; - delete context.payload.repository.name; - await expect( extractInputs(github, context, createMockCore()), ).rejects.toThrow("from context payload"); diff --git a/.github/workflows/test/sdk-breaking-change-labels.test.js b/.github/workflows/test/sdk-breaking-change-labels.test.js index 207cde7bfb1a..0385fe4e1585 100644 --- a/.github/workflows/test/sdk-breaking-change-labels.test.js +++ b/.github/workflows/test/sdk-breaking-change-labels.test.js @@ -33,8 +33,8 @@ describe("sdk-breaking-change-labels", () => { it("should extract inputs and call getLabelAndActionImpl", async () => { // Mock extracted inputs const mockInputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; @@ -62,18 +62,11 @@ describe("sdk-breaking-change-labels", () => { JSON.stringify({ labelAction: true, language, + prNumber: "123", }), ), }; - // Mock PR search results - mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ - data: { - total_count: 1, - items: [{ number: 123, html_url: "https://github.com/pr/123" }], - }, - }); - // Setup fetch to return different responses for each call global.fetch.mockImplementation((url) => { if (url.includes("artifacts?artifactName=")) { @@ -96,19 +89,12 @@ describe("sdk-breaking-change-labels", () => { labelAction: LabelAction.Add, issueNumber: 123, }); - - // Verify mocks were called correctly - expect(mockGithub.rest.search.issuesAndPullRequests).toHaveBeenCalledWith( - { - q: `sha:abc123 type:pr state:open`, - }, - ); }); it("should correctly set labelAction to Remove", async () => { // Setup inputs const inputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; @@ -133,6 +119,7 @@ describe("sdk-breaking-change-labels", () => { JSON.stringify({ labelAction: false, language, + prNumber: "123", }), ), }; @@ -146,14 +133,6 @@ describe("sdk-breaking-change-labels", () => { } }); - // Mock PR search - mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ - data: { - total_count: 1, - items: [{ number: 123, html_url: "https://github.com/pr/123" }], - }, - }); - // Call function const result = await getLabelAndAction({ github: mockGithub, @@ -171,8 +150,7 @@ describe("sdk-breaking-change-labels", () => { it("should throw error with invalid inputs", async () => { // Setup inputs const inputs = { - ado_build_id: "", - ado_project_url: "https://dev.azure.com/project", + details_url: "", head_sha: "abc123", }; @@ -195,8 +173,8 @@ describe("sdk-breaking-change-labels", () => { it("should handle API failure", async () => { // Setup inputs const inputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; @@ -208,18 +186,9 @@ describe("sdk-breaking-change-labels", () => { text: vi.fn().mockResolvedValue("Artifact not found"), }); - // Mock PR search success - mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ - data: { - total_count: 1, - items: [{ number: 123, html_url: "https://github.com/pr/123" }], - }, - }); - // Call function const result = await getLabelAndActionImpl({ - ado_build_id: inputs.ado_build_id, - ado_project_url: inputs.ado_project_url, + details_url: inputs.details_url, head_sha: inputs.head_sha, github: mockGithub, core: mockCore, @@ -241,31 +210,60 @@ describe("sdk-breaking-change-labels", () => { it("should complete without op when artifact does not exist", async () => { // Setup inputs const inputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; - // Mock fetch failure - global.fetch.mockResolvedValue({ - ok: false, - status: 404, - statusText: "Not Found", - text: vi.fn().mockResolvedValue("Artifact not found"), - }); - - // Mock PR search success - mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ - data: { - total_count: 1, - items: [{ number: 123, html_url: "https://github.com/pr/123" }], - }, + // Mock fetch to handle the artifact URL with 404 error and fallback behavior + global.fetch.mockImplementation((url) => { + if (url.includes("artifacts?artifactName=spec-gen-sdk-artifact")) { + // Initial fetch for the specific artifact returns 404 + return Promise.resolve({ + ok: false, + status: 404, + statusText: "Not Found", + text: vi.fn().mockResolvedValue("Artifact not found"), + }); + } else if ( + url.includes("artifacts?api-version=7.0") && + !url.includes("artifactName=") + ) { + // List artifacts API call (used by fetchFailedArtifact) + return Promise.resolve({ + ok: true, + json: vi.fn().mockResolvedValue({ + value: [ + { + name: "spec-gen-sdk-artifact-failed", + id: "12345", + resource: { + downloadUrl: + "https://dev.azure.com/download-failed?format=zip", + }, + }, + ], + }), + }); + } else if (url.includes("artifactName=spec-gen-sdk-artifact-failed")) { + // Fetch for the failed artifact version returns 404 too + return Promise.resolve({ + ok: false, + status: 404, + statusText: "Not Found", + text: vi.fn().mockResolvedValue("Failed artifact not found either"), + }); + } + // Default response for other URLs + return Promise.resolve({ + ok: true, + text: vi.fn().mockResolvedValue("{}"), + }); }); // Call function const result = await getLabelAndActionImpl({ - ado_build_id: inputs.ado_build_id, - ado_project_url: inputs.ado_project_url, + details_url: inputs.details_url, head_sha: inputs.head_sha, github: mockGithub, core: mockCore, @@ -282,8 +280,8 @@ describe("sdk-breaking-change-labels", () => { it("should throw error if resource is empty from the artifact api response", async () => { // Setup inputs const inputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; @@ -303,8 +301,7 @@ describe("sdk-breaking-change-labels", () => { // Call function and expect it to throw await expect( getLabelAndActionImpl({ - ado_build_id: inputs.ado_build_id, - ado_project_url: inputs.ado_project_url, + details_url: inputs.details_url, head_sha: inputs.head_sha, github: mockGithub, core: mockCore, @@ -315,8 +312,8 @@ describe("sdk-breaking-change-labels", () => { it("should throw error if missing download url from the artifact api response", async () => { // Setup inputs const inputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; @@ -338,8 +335,7 @@ describe("sdk-breaking-change-labels", () => { // Call function and expect it to throw await expect( getLabelAndActionImpl({ - ado_build_id: inputs.ado_build_id, - ado_project_url: inputs.ado_project_url, + details_url: inputs.details_url, head_sha: inputs.head_sha, github: mockGithub, core: mockCore, @@ -350,8 +346,8 @@ describe("sdk-breaking-change-labels", () => { it("should throw error when fail to fetch artifact content", async () => { // Setup inputs const inputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; @@ -387,8 +383,7 @@ describe("sdk-breaking-change-labels", () => { // Call function and expect it to throw await expect( getLabelAndActionImpl({ - ado_build_id: inputs.ado_build_id, - ado_project_url: inputs.ado_project_url, + details_url: inputs.details_url, head_sha: inputs.head_sha, github: mockGithub, core: mockCore, @@ -399,8 +394,8 @@ describe("sdk-breaking-change-labels", () => { it("should handle exception during processing", async () => { // Setup inputs const inputs = { - ado_build_id: "12345", - ado_project_url: "https://dev.azure.com/project", + details_url: + "https://dev.azure.com/project/_build/results?buildId=12345", head_sha: "abc123", }; @@ -409,18 +404,9 @@ describe("sdk-breaking-change-labels", () => { throw new Error("Network error"); }); - // Mock PR search success - mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ - data: { - total_count: 1, - items: [{ number: 123, html_url: "https://github.com/pr/123" }], - }, - }); - // Start the async operation that will retry const promise = getLabelAndActionImpl({ - ado_build_id: inputs.ado_build_id, - ado_project_url: inputs.ado_project_url, + details_url: inputs.details_url, head_sha: inputs.head_sha, github: mockGithub, core: mockCore, diff --git a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml index 220840821afc..764f9e421e0b 100644 --- a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml +++ b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml @@ -200,64 +200,35 @@ stages: ArtifactPath: $(StagedArtifactsFolder) CustomCondition: and(succeededOrFailed(), ne(variables['StagedArtifactsFolder'], '')) - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - - task: PowerShell@2 - displayName: Add label to the spec PR - condition: and(eq(variables['Build.Reason'], 'PullRequest'), ne(variables['BreakingChangeLabel'], ''), eq(variables['BreakingChangeLabelAction'], 'add')) - inputs: - pwsh: true - workingDirectory: $(SdkRepoDirectory) - filePath: $(SdkRepoDirectory)/eng/common/scripts/Add-IssueLabels.ps1 - arguments: > - -RepoOwner $(SpecRepoOwner) - -RepoName $(SpecRepoName) - -IssueNumber "$(System.PullRequest.PullRequestNumber)" - -Labels $(BreakingChangeLabel) - -AuthToken "$(azuresdk-github-pat)" + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.SkipPullRequestCreation, false), ne(variables['Build.Reason'], 'PullRequest')) }}: + - template: /eng/common/pipelines/templates/steps/git-push-changes.yml + parameters: + BaseRepoBranch: $(PrBranch)-$(Build.BuildId) + BaseRepoOwner: azure-sdk + CommitMsg: $(GeneratedSDKInformation) + TargetRepoOwner: $(SdkRepoOwner) + TargetRepoName: $(SdkRepoName) + PushArgs: "--force" + WorkingDirectory: $(SdkRepoDirectory) + ScriptDirectory: $(SdkRepoDirectory)/eng/common/scripts - task: PowerShell@2 - displayName: Remove label from the spec PR - condition: and(eq(variables['Build.Reason'], 'PullRequest'), ne(variables['BreakingChangeLabel'], ''), eq(variables['BreakingChangeLabelAction'], 'remove')) + displayName: Create pull request + condition: and(succeeded(), eq(variables['HasChanges'], 'true'), ne(variables['Build.Reason'], 'PullRequest'), not(endsWith(variables['SdkRepoName'], '-pr'))) inputs: pwsh: true workingDirectory: $(SdkRepoDirectory) - filePath: $(SdkRepoDirectory)/eng/common/scripts/Remove-IssueLabel.ps1 + filePath: $(SdkRepoDirectory)/eng/common/scripts/Submit-PullRequest.ps1 arguments: > - -RepoOwner $(SpecRepoOwner) - -RepoName $(SpecRepoName) - -IssueNumber "$(System.PullRequest.PullRequestNumber)" - -LabelName $(BreakingChangeLabel) + -RepoOwner "$(SdkRepoOwner)" + -RepoName "$(SdkRepoName)" + -BaseBranch "main" + -PROwner "azure-sdk" + -PRBranch "$(PrBranch)-$(Build.BuildId)" -AuthToken "$(azuresdk-github-pat)" - - - ${{ if and(eq(parameters.SkipPullRequestCreation, false), ne(variables['Build.Reason'], 'PullRequest')) }}: - - template: /eng/common/pipelines/templates/steps/git-push-changes.yml - parameters: - BaseRepoBranch: $(PrBranch)-$(Build.BuildId) - BaseRepoOwner: azure-sdk - CommitMsg: $(GeneratedSDKInformation) - TargetRepoOwner: $(SdkRepoOwner) - TargetRepoName: $(SdkRepoName) - PushArgs: "--force" - WorkingDirectory: $(SdkRepoDirectory) - ScriptDirectory: $(SdkRepoDirectory)/eng/common/scripts - - - task: PowerShell@2 - displayName: Create pull request - condition: and(succeeded(), eq(variables['HasChanges'], 'true'), ne(variables['Build.Reason'], 'PullRequest'), not(endsWith(variables['SdkRepoName'], '-pr'))) - inputs: - pwsh: true - workingDirectory: $(SdkRepoDirectory) - filePath: $(SdkRepoDirectory)/eng/common/scripts/Submit-PullRequest.ps1 - arguments: > - -RepoOwner "$(SdkRepoOwner)" - -RepoName "$(SdkRepoName)" - -BaseBranch "main" - -PROwner "azure-sdk" - -PRBranch "$(PrBranch)-$(Build.BuildId)" - -AuthToken "$(azuresdk-github-pat)" - -PRTitle "$(PrTitle)-generated-from-$(Build.DefinitionName)-$(Build.BuildId)" - -PRBody "$(GeneratedSDKInformation)" - -OpenAsDraft $true + -PRTitle "$(PrTitle)-generated-from-$(Build.DefinitionName)-$(Build.BuildId)" + -PRBody "$(GeneratedSDKInformation)" + -OpenAsDraft $true - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: - pwsh: | 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 4b924ce3fc02..dcaa1cc2c0a6 100644 --- a/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts +++ b/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts @@ -309,6 +309,7 @@ export function generateArtifact( const artifactInfo: SpecGenSdkArtifactInfo = { language: commandInput.sdkLanguage, result, + prNumber: commandInput.prNumber, labelAction: hasBreakingChange, isSpecGenSdkCheckRequired: hasManagementPlaneSpecs && SpecGenSdkRequiredSettings[commandInput.sdkLanguage as SdkName], diff --git a/eng/tools/spec-gen-sdk-runner/src/types.ts b/eng/tools/spec-gen-sdk-runner/src/types.ts index dc8841e8596f..5417d19d7b63 100644 --- a/eng/tools/spec-gen-sdk-runner/src/types.ts +++ b/eng/tools/spec-gen-sdk-runner/src/types.ts @@ -44,6 +44,7 @@ export type VsoLogs = Map< export interface SpecGenSdkArtifactInfo { language: string; result: string; + prNumber?: string; labelAction?: boolean; isSpecGenSdkCheckRequired: boolean; apiViewRequestData: APIViewRequestData [];