eng/tools/lint-diff/src/runChecks.ts (98 lines of code) (raw):
import { join } from "path";
import { exec, ExecException } from "node:child_process";
import { getOpenapiType } from "./markdown-utils.js";
import { getPathToDependency, isFailure } from "./util.js";
import { AutoRestMessage, AutorestRunResult } from "./lintdiff-types.js";
const MAX_EXEC_BUFFER = 64 * 1024 * 1024;
// AutoRest messages are JSON objects that start with the string '{"level":'
// Non-AutoRest messages will start with things like '{"pluginName"'
const AUTOREST_ERROR_PREFIX = '{"level":';
export async function runChecks(
path: string,
runList: Map<string, string[]>,
): Promise<AutorestRunResult[]> {
const dependenciesDir = await getPathToDependency("@microsoft.azure/openapi-validator");
const result: AutorestRunResult[] = [];
for (const [readme, tags] of runList.entries()) {
const changedFilePath = join(path, readme);
// TODO: Move this into getRunList
let openApiType = await getOpenapiType(changedFilePath);
// From momentOfTruth.ts:executeAutoRestWithLintDiff
// This is a quick workaround for https://github.com/Azure/azure-sdk-tools/issues/6549
// We override the openapi-subtype with the value of openapi-type,
// to prevent LintDiff from reading openapi-subtype from the AutoRest config file (README)
// and overriding openapi-type with it.
let openApiSubType = openApiType;
// If the tags array is empty run the loop once but with a null tag
const coalescedTags = tags?.length ? tags : [null];
for (const tag of coalescedTags) {
let tagArg = tag ? `--tag=${tag} ` : "";
let autorestCommand =
`npm exec --no -- autorest ` +
`--v3 ` +
`--spectral ` +
`--azure-validator ` +
`--semantic-validator=false ` +
`--model-validator=false ` +
`--message-format=json ` +
`--openapi-type=${openApiType} ` +
`--openapi-subtype=${openApiSubType} ` +
`--use=${dependenciesDir} ` +
`${tagArg} ` +
`${changedFilePath}`;
console.log(`::group::Autorest for type: ${openApiType} readme: ${readme} tag: ${tag}`);
console.log(`\tAutorest command: ${autorestCommand}`);
const executionResult = await executeCommand(autorestCommand);
console.log(executionResult.stderr + executionResult.stdout);
const lintDiffResult = {
autorestCommand,
rootPath: path,
readme,
tag: tag ? tag : "",
openApiType,
...executionResult,
};
logAutorestExecutionErrors(lintDiffResult);
console.log("::endgroup::");
result.push(lintDiffResult);
console.log(`\tAutorest result length: ${lintDiffResult.stderr.length + lintDiffResult.stdout.length}\n`);
}
}
return result;
}
export async function executeCommand(
command: string,
cwd: string = ".",
): Promise<{ error: ExecException | null; stdout: string; stderr: string }> {
return new Promise((resolve) => {
exec(
command,
{ cwd, encoding: "utf-8", maxBuffer: MAX_EXEC_BUFFER },
(error, stdout, stderr) => {
resolve({ error, stdout, stderr });
},
);
});
}
export function getAutorestErrors(runResult: AutorestRunResult): AutoRestMessage[] {
const errors = [];
const lines = (runResult.stdout + runResult.stderr).split("\n").map((line) => line.trim());
for (const line of lines) {
if (line.startsWith(AUTOREST_ERROR_PREFIX)) {
const error = JSON.parse(line) as AutoRestMessage;
if (isFailure(error.level)) {
errors.push(error);
}
}
}
return errors;
}
export function logAutorestExecutionErrors(runResult: AutorestRunResult) {
if (runResult.error !== null) {
const autoRestPrefix = `{`;
const stdoutContainsLevelError = runResult.stdout.includes(`${autoRestPrefix}"level":"error"`);
const stdoutContainsLevelFatal = runResult.stdout.includes(`${autoRestPrefix}"level":"fatal"`);
const stderrContainsLevelError = runResult.stderr.includes(`${autoRestPrefix}"level":"error"`);
const stderrContainsLevelFatal = runResult.stderr.includes(`${autoRestPrefix}"level":"fatal"`);
console.log(
`\tAutorest completed with errors:
\t\tExit code: ${runResult.error.code}
\t\tError is not null: true,
\t\tstdout contains AutoRest 'error': ${stdoutContainsLevelError}
\t\tstdout contains AutoRest 'fatal': ${stdoutContainsLevelFatal}
\t\tstderr contains AutoRest 'error': ${stderrContainsLevelError}
\t\tstderr contains AutoRest 'fatal': ${stderrContainsLevelFatal}`,
);
}
}