From 7efdb892a93c94491ffeeb8a271d6041b7e73356 Mon Sep 17 00:00:00 2001 From: v-tianxi Date: Fri, 7 Mar 2025 14:31:07 +0800 Subject: [PATCH 1/2] export log issues to ado --- eng/tools/spec-gen-sdk-runner/src/commands.ts | 73 ++++++++++++++++++- eng/tools/spec-gen-sdk-runner/src/types.ts | 5 ++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/eng/tools/spec-gen-sdk-runner/src/commands.ts b/eng/tools/spec-gen-sdk-runner/src/commands.ts index 012a837b66b0..ff67cf411c3f 100644 --- a/eng/tools/spec-gen-sdk-runner/src/commands.ts +++ b/eng/tools/spec-gen-sdk-runner/src/commands.ts @@ -8,8 +8,8 @@ import { getAllTypeSpecPaths, resetGitRepo, } from "./utils.js"; -import { LogLevel, logMessage, vsoAddAttachment } from "./log.js"; -import { SpecGenSdkCmdInput } from "./types.js"; +import { LogLevel, logMessage, vsoAddAttachment, vsoLogIssue } from "./log.js"; +import { CommandLog, SpecGenSdkCmdInput } from "./types.js"; import { detectChangedSpecConfigFiles } from "./change-files.js"; export async function generateSdkForSingleSpec(): Promise { @@ -43,6 +43,9 @@ export async function generateSdkForSingleSpec(): Promise { statusCode = 1; } logMessage("ending group logging", LogLevel.EndGroup); + + logIssuesToADO(commandInput, specConfigPath || ''); + return statusCode; } @@ -105,6 +108,8 @@ export async function generateSdkForSpecPr(): Promise { statusCode = 1; } logMessage("ending group logging", LogLevel.EndGroup); + + logIssuesToADO(commandInput, changedSpecPath || ''); } return statusCode; } @@ -180,6 +185,8 @@ export async function generateSdkForBatchSpecs(runMode: string): Promise statusCode = 1; } logMessage("ending group logging", LogLevel.EndGroup); + + logIssuesToADO(commandInput, specConfigPath || ''); } if (failedCount > 0) { markdownContent += `${failedContent}\n`; @@ -325,3 +332,65 @@ function getSpecPaths(runMode: string, specRepoPath: string): string[] { } return specConfigPaths; } + +/** + * Extract and format the prefix from tspConfigPath or readmePath. + * @param {string | undefined} tspConfigPath The tspConfigPath to extract the prefix from. + * @param {string | undefined} readmePath The readmePath to extract the prefix from. + * @returns {string} The formatted prefix. + */ +function extractPathFromSpecConfig(tspConfigPath: string | undefined, readmePath: string | undefined): string { + let prefix = ''; + if (tspConfigPath) { + const match = tspConfigPath.match(/specification\/(.+)\/tspconfig\.yaml$/); + if (match) { + const segments = match[1].split('/'); + prefix = segments.join('-').toLowerCase().replace(/\./g, '-'); + } + } else if (readmePath) { + const match = readmePath.match(/specification\/(.+?)\/readme\.md$/i); + if (match) { + const segments = match[1].split('/'); + prefix = segments.join('-').toLowerCase().replace(/\./g, '-'); + } + } + return prefix; +} + +/** + * Generates file paths for different log files based on the given command input. + * @param {SpecGenSdkCmdInput} commandInput + * @returns An object containing the full log file path, filtered log file path, and HTML log file path. + */ +function getLogsPath(commandInput: SpecGenSdkCmdInput): { fullLogFileName: string, filteredLogFileName: string, htmlLogFileName: string } { + const fileNamePrefix = extractPathFromSpecConfig(commandInput.tspConfigPath, commandInput.readmePath) + const logFolder = path.join(commandInput.workingFolder, 'out/logs'); + const fullLogFileName = path.join(logFolder, `${fileNamePrefix}-full.log`); + const filteredLogFileName = path.join(logFolder, `${fileNamePrefix}-filtered.log`); + const htmlLogFileName = path.join(logFolder, `${fileNamePrefix}-${commandInput.sdkRepoName.substring("azure-sdk-for-".length)}-gen-result.html`); + return { fullLogFileName, filteredLogFileName, htmlLogFileName }; +} + +/** + * Logs SDK generation issues to Azure DevOps (ADO) + * @param commandInput + * @param specConfigPath + */ +function logIssuesToADO(commandInput: SpecGenSdkCmdInput, specConfigPath: string): void { + const logPath = getLogsPath(commandInput).filteredLogFileName; + let logIssues:CommandLog[] = [] + try { + const log = JSON.parse(fs.readFileSync(logPath, "utf8")); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + logIssues = log.logIssues; + } catch (error) { + logMessage(`Error reading log at ${logPath}:${error}`, LogLevel.Error); + } + + if(logIssues.length > 0) { + logMessage(`Error generating SDK from ${specConfigPath}`, LogLevel.Group); + const logIssuesList = logIssues.flatMap(entry => [`Get log issues from script ${entry.command} `, ...entry.logIssues]);; + vsoLogIssue(logIssuesList.join('%0D%0A')); + logMessage("ending group logging", LogLevel.EndGroup); + } +} \ No newline at end of file diff --git a/eng/tools/spec-gen-sdk-runner/src/types.ts b/eng/tools/spec-gen-sdk-runner/src/types.ts index 66eac31f6576..3cc2270e9bb4 100644 --- a/eng/tools/spec-gen-sdk-runner/src/types.ts +++ b/eng/tools/spec-gen-sdk-runner/src/types.ts @@ -16,3 +16,8 @@ export interface SpecGenSdkCmdInput { headRepoHttpsUrl?: string; headBranch?: string; } + +export type CommandLog = { + command: string; + logIssues: string[]; +}; \ No newline at end of file From a77c986a01670e03395f7e5c587f616876c9357b Mon Sep 17 00:00:00 2001 From: v-tianxi Date: Mon, 10 Mar 2025 20:32:42 +0800 Subject: [PATCH 2/2] fix comment --- eng/tools/spec-gen-sdk-runner/src/commands.ts | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/eng/tools/spec-gen-sdk-runner/src/commands.ts b/eng/tools/spec-gen-sdk-runner/src/commands.ts index ff67cf411c3f..b950f9870c10 100644 --- a/eng/tools/spec-gen-sdk-runner/src/commands.ts +++ b/eng/tools/spec-gen-sdk-runner/src/commands.ts @@ -15,7 +15,7 @@ import { detectChangedSpecConfigFiles } from "./change-files.js"; export async function generateSdkForSingleSpec(): Promise { // Parse the arguments const commandInput: SpecGenSdkCmdInput = parseArguments(); - const specConfigPath = commandInput.tspConfigPath ?? commandInput.readmePath; + const specConfigPath = `${commandInput.tspConfigPath} ${commandInput.readmePath}`; // Construct the spec-gen-sdk command const specGenSdkCommand = prepareSpecGenSdkCommand(commandInput); logMessage(`Generating SDK from ${specConfigPath}`, LogLevel.Group); @@ -44,7 +44,7 @@ export async function generateSdkForSingleSpec(): Promise { } logMessage("ending group logging", LogLevel.EndGroup); - logIssuesToADO(commandInput, specConfigPath || ''); + logIssuesToPipeline(commandInput, specConfigPath); return statusCode; } @@ -74,7 +74,7 @@ export async function generateSdkForSpecPr(): Promise { specGenSdkCommand.push("--readme-relative-path", changedSpec.readmeMd); pushedSpecConfigCount++; } - const changedSpecPath = changedSpec.typespecProject ?? changedSpec.readmeMd; + const changedSpecPath = `${changedSpec.typespecProject} ${changedSpec.readmeMd}`; logMessage(`Generating SDK from ${changedSpecPath}`, LogLevel.Group); logMessage(`Command:${specGenSdkCommand.join(" ")}`); @@ -109,7 +109,7 @@ export async function generateSdkForSpecPr(): Promise { } logMessage("ending group logging", LogLevel.EndGroup); - logIssuesToADO(commandInput, changedSpecPath || ''); + logIssuesToPipeline(commandInput, changedSpecPath); } return statusCode; } @@ -186,7 +186,7 @@ export async function generateSdkForBatchSpecs(runMode: string): Promise } logMessage("ending group logging", LogLevel.EndGroup); - logIssuesToADO(commandInput, specConfigPath || ''); + logIssuesToPipeline(commandInput, specConfigPath || ''); } if (failedCount > 0) { markdownContent += `${failedContent}\n`; @@ -335,6 +335,8 @@ function getSpecPaths(runMode: string, specRepoPath: string): string[] { /** * Extract and format the prefix from tspConfigPath or readmePath. + * This function is copied from 'spec-gen-sdk'. + * Source code: [Azure SDK Tools - spec-gen-sdk](https://github.com/Azure/azure-sdk-tools/blob/main/tools/spec-gen-sdk/src/utils/utils.ts#L171) * @param {string | undefined} tspConfigPath The tspConfigPath to extract the prefix from. * @param {string | undefined} readmePath The readmePath to extract the prefix from. * @returns {string} The formatted prefix. @@ -342,42 +344,32 @@ function getSpecPaths(runMode: string, specRepoPath: string): string[] { function extractPathFromSpecConfig(tspConfigPath: string | undefined, readmePath: string | undefined): string { let prefix = ''; if (tspConfigPath) { - const match = tspConfigPath.match(/specification\/(.+)\/tspconfig\.yaml$/); + const regex = /specification\/(.+)\/tspconfig\.yaml$/ + const match = regex.exec(tspConfigPath); if (match) { const segments = match[1].split('/'); - prefix = segments.join('-').toLowerCase().replace(/\./g, '-'); + prefix = segments.join('-').toLowerCase().replaceAll('.', '-'); } } else if (readmePath) { - const match = readmePath.match(/specification\/(.+?)\/readme\.md$/i); + const regex = /specification\/(.+?)\/readme\.md$/i + const match = regex.exec(readmePath); if (match) { const segments = match[1].split('/'); - prefix = segments.join('-').toLowerCase().replace(/\./g, '-'); + prefix = segments.join('-').toLowerCase().replaceAll('.', '-'); } } return prefix; } -/** - * Generates file paths for different log files based on the given command input. - * @param {SpecGenSdkCmdInput} commandInput - * @returns An object containing the full log file path, filtered log file path, and HTML log file path. - */ -function getLogsPath(commandInput: SpecGenSdkCmdInput): { fullLogFileName: string, filteredLogFileName: string, htmlLogFileName: string } { - const fileNamePrefix = extractPathFromSpecConfig(commandInput.tspConfigPath, commandInput.readmePath) - const logFolder = path.join(commandInput.workingFolder, 'out/logs'); - const fullLogFileName = path.join(logFolder, `${fileNamePrefix}-full.log`); - const filteredLogFileName = path.join(logFolder, `${fileNamePrefix}-filtered.log`); - const htmlLogFileName = path.join(logFolder, `${fileNamePrefix}-${commandInput.sdkRepoName.substring("azure-sdk-for-".length)}-gen-result.html`); - return { fullLogFileName, filteredLogFileName, htmlLogFileName }; -} - /** * Logs SDK generation issues to Azure DevOps (ADO) * @param commandInput * @param specConfigPath */ -function logIssuesToADO(commandInput: SpecGenSdkCmdInput, specConfigPath: string): void { - const logPath = getLogsPath(commandInput).filteredLogFileName; +function logIssuesToPipeline(commandInput: SpecGenSdkCmdInput, specConfigPath: string): void { + const fileNamePrefix = extractPathFromSpecConfig(commandInput.tspConfigPath, commandInput.readmePath) + const logFolder = path.join(commandInput.workingFolder, 'out/logs'); + const logPath = path.join(logFolder, `${fileNamePrefix}-filtered.log`); let logIssues:CommandLog[] = [] try { const log = JSON.parse(fs.readFileSync(logPath, "utf8"));