Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f24f5c6
remove dep @azure/avocado
mikeharder Oct 14, 2025
80102b2
add avocado to eng/tools/tsconfig.json
mikeharder Oct 14, 2025
32695ca
add package.json
mikeharder Oct 14, 2025
d7a913e
add tsconfig.json
mikeharder Oct 14, 2025
1c35673
copy source files from Azure/avocado
mikeharder Oct 14, 2025
b2d030b
update src for new prettier config
mikeharder Oct 14, 2025
ca6269f
update tsconfig.json for prettier
mikeharder Oct 14, 2025
1a45d45
add eslint.config.js
mikeharder Oct 14, 2025
16aa64f
remove unused suppression
mikeharder Oct 14, 2025
aa5a3b0
copy folder src/test from Azure/avocado
mikeharder Oct 14, 2025
d33fe40
add cmd script
mikeharder Oct 14, 2025
6a154a2
add missing test
mikeharder Oct 14, 2025
6e9a542
add missing tests
mikeharder Oct 14, 2025
a3340eb
rename tests
mikeharder Oct 14, 2025
83bcce3
format
mikeharder Oct 14, 2025
897fb70
format
mikeharder Oct 14, 2025
52e6d10
add workflow avocado-test
mikeharder Oct 14, 2025
a7a593a
delete tmp folder
mikeharder Oct 14, 2025
4de0d9d
rename job
mikeharder Oct 14, 2025
e083f40
add avocado to eng/tools/package.json
mikeharder Oct 14, 2025
851b997
npm i
mikeharder Oct 14, 2025
7901917
move test outside src
mikeharder Oct 14, 2025
9aff1d6
update test code for paths
mikeharder Oct 14, 2025
ef83713
remove avocado-tmp
mikeharder Oct 14, 2025
4ee1ed1
move test specs to "fixtures" folder
mikeharder Oct 14, 2025
393de44
delete avocado-tmp
mikeharder Oct 14, 2025
a73213e
fix paths in cmd script
mikeharder Oct 14, 2025
212f11f
increase test timeout
mikeharder Oct 14, 2025
3eac55c
[getInputFilesFromReadme] Return no files, instead of throwing, on in…
mikeharder Oct 15, 2025
832ec1a
update yargs from 15.4.1 to 18.0.0
mikeharder Oct 29, 2025
55df00c
npm i
mikeharder Oct 29, 2025
0e30204
Merge branch 'main' into migrate-avocado
mikeharder Oct 29, 2025
272db29
Merge branch 'main' into migrate-avocado
mikeharder Oct 29, 2025
6be8ae4
npm update
mikeharder Oct 29, 2025
4a1d3c9
Merge branch 'main' into migrate-avocado
mikeharder Oct 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/avocado-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Avocado - Test

on:
push:
branches:
- main
- typespec-next
pull_request:
paths:
- package-lock.json
- package.json
- tsconfig.json
- .github/workflows/_reusable-eng-tools-test.yaml
- .github/workflows/avocado-test.yaml
- eng/tools/package.json
- eng/tools/tsconfig.json
- eng/tools/avocado/**
workflow_dispatch:

permissions:
contents: read

jobs:
avocado:
uses: ./.github/workflows/_reusable-eng-tools-test.yaml
with:
package: avocado
lint: true
29 changes: 29 additions & 0 deletions eng/tools/avocado/cmd/avocado.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node

import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { run } from "../dist/src/cli.js";
import { avocado, UnifiedPipelineReport } from "../dist/src/index.js";

var argv = yargs(hideBin(process.argv))
.usage("Usage: avocado [options]")
.alias("f", "file")
.describe("f", "output detail result to log file")
.alias("d", "dir")
.describe("d", "run avocado under directory")
.option("excludePaths", {
type: "array",
desc: "array contains path patterns to be ignored",
})
.option("includePaths", {
type: "array",
desc: "array contains path patterns to be included. If this option is not set, all files will be included. If this option is set, only files that match at least one pattern will be included",
})
.help("h")
.alias("h", "help").argv;

run(avocado, UnifiedPipelineReport(argv.f), {
cwd: process.cwd(),
env: process.env,
args: { dir: argv.d, excludePaths: argv.excludePaths, includePaths: argv.includePaths },
});
18 changes: 18 additions & 0 deletions eng/tools/avocado/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import eslint from "@eslint/js";
import { defineConfig } from "eslint/config";
import globals from "globals";
import tseslint from "typescript-eslint";

/** @type {import('eslint').Linter.Config[]} */
export default defineConfig(
{ ignores: ["dist/**"] },
eslint.configs.recommended,
tseslint.configs.recommended,
{
languageOptions: { globals: globals.node },
rules: {
// 17 errors
"@typescript-eslint/no-explicit-any": "off",
},
},
);
52 changes: 52 additions & 0 deletions eng/tools/avocado/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "@azure-tools/avocado",
"private": true,
"type": "module",
"main": "dist/src/index.js",
"bin": {
"avocado": "cmd/avocado.js"
},
"scripts": {
"build": "tsc --build",
"format": "prettier . --ignore-path ../.prettierignore --write",
"format:check": "prettier . --ignore-path ../.prettierignore --check",
"format:check:ci": "prettier . --ignore-path ../.prettierignore --check --log-level debug",
"lint": "cross-env DEBUG=eslint:eslint eslint",
"test": "vitest",
"test:ci": "vitest run --coverage --reporter=verbose"
},
"engines": {
"node": ">=20.0.0"
},
"dependencies": {
"@azure/openapi-markdown": "0.9.4",
"@azure/swagger-validation-common": "0.1.2",
"@ts-common/async-iterator": "^1.1.0",
"@ts-common/commonmark-to-markdown": "^2.0.2",
"@ts-common/fs": "^1.1.0",
"@ts-common/iterator": "^1.1.2",
"@ts-common/json": "^1.1.0",
"@ts-common/json-parser": "^1.1.0",
"@ts-common/string-map": "^1.1.1",
"commonmark": "0.31.2",
"glob": "^11.0.3",
"js-yaml": "^4.1.0",
"jsonpath-plus": "^10.0.0",
"node-object-hash": "^3.1.1",
"yargs": "^18.0.0"
},
"devDependencies": {
"@eslint/js": "^9.37.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.0.0",
"@types/yargs": "^17.0.33",
"@vitest/coverage-v8": "^3.2.4",
"cross-env": "^10.1.0",
"eslint": "^9.37.0",
"prettier": "~3.6.2",
"prettier-plugin-organize-imports": "^4.3.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.0",
"vitest": "^3.2.4"
}
}
23 changes: 23 additions & 0 deletions eng/tools/avocado/src/child-process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

import * as childProcess from "child_process";
import * as util from "util";

const nodeJsExec = util.promisify(childProcess.exec);

export type ExecResult = {
/**
* Standard Output
*/
readonly stdout: string;
/**
* Standard Error
*/
readonly stderr: string;
};

export const exec = (
command: string,
options: childProcess.ExecOptionsWithStringEncoding,
): Promise<ExecResult> => nodeJsExec(command, { maxBuffer: Infinity, ...options });
83 changes: 83 additions & 0 deletions eng/tools/avocado/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

import * as stringMap from "@ts-common/string-map";
import { IErrorBase } from "./errors.js";

export type Report = {
/**
* This is a callback function to report validation tools result.
*/
readonly logResult: (error: any) => void;
/**
* This is a callback function to report validation tools exception.
*/
readonly logError: (error: any) => void;
/**
* This is a callback function to report an info.
*/
readonly logInfo: (info: any) => void;
};

export type Config = {
/**
* Current working directory.
*/
readonly cwd: string;
/**
* Environment variables.
*/
readonly env: stringMap.StringMap<string>;
/**
* Arguments
*/
readonly args?: stringMap.StringMap<any>;
};

export const defaultConfig = () => ({
cwd: process.cwd(),
env: process.env,
args: {},
});

export const isAzurePipelineEnv = (): boolean =>
process.env.SYSTEM_PULLREQUEST_TARGETBRANCH !== undefined;

/**
* The function executes the given `tool` and prints errors to `stderr`.
*
* @param tool is a function which returns errors as `AsyncIterable`.
*/
export const run = async <T extends IErrorBase>(
tool: (config: Config) => AsyncIterable<T>,
report: Report = { logResult: console.log, logError: console.error, logInfo: console.log },
config: Config = defaultConfig(),
): Promise<void> => {
try {
const errors = tool(config);
let errorsNumber = 0;
for await (const e of errors) {
errorsNumber += e.level !== "Warning" && e.level !== "Info" ? 1 : 0;
report.logResult(e);
}
report.logInfo(`errors: ${errorsNumber}`);
if (errorsNumber > 0) {
if (isAzurePipelineEnv()) {
console.log("##vso[task.setVariable variable=ValidationResult]failure");
}
process.exitCode = 1;
} else {
if (isAzurePipelineEnv()) {
console.log("##vso[task.setVariable variable=ValidationResult]success");
}
process.exitCode = 0;
}
} catch (e) {
report.logInfo(`INTERNAL ERROR`);
if (isAzurePipelineEnv()) {
console.log("##vso[task.setVariable variable=ValidationResult]failure");
}
report.logError(e);
process.exitCode = 1;
}
};
Loading