@azure/avocado
Advanced tools
Comparing version 0.6.2 to 0.6.3
# Changelog | ||
## 0.6.3 | ||
- Add rule MISSING_README to validate each RP folder must have readme.md. | ||
- Modify unit test. | ||
## 0.6.2 | ||
- Support $(this-folder) | ||
## 0.6.1 | ||
@@ -4,0 +13,0 @@ |
import * as cli from './cli'; | ||
import * as err from './errors'; | ||
import * as asyncIt from '@ts-common/async-iterator'; | ||
@@ -44,2 +45,4 @@ export declare type FileChangeKind = 'Added' | 'Deleted' | 'Modified'; | ||
}; | ||
export declare const hasCommonRPFolder: (pathA: string, pathB: string) => boolean; | ||
export declare const isPRRelatedError: (fileChanges: readonly FileChange[], error: err.Error) => boolean; | ||
/** | ||
@@ -46,0 +49,0 @@ * If the function is called in Azure DevOps CI for a Pull Request, it creates a |
@@ -21,2 +21,14 @@ "use strict"; | ||
}; | ||
exports.hasCommonRPFolder = (pathA, pathB) => { | ||
const regex = new RegExp(/specification\/(\w)+\//); | ||
const matchA = pathA.match(regex); | ||
const matchB = pathB.match(regex); | ||
return matchA !== null && matchB !== null && matchA[0] === matchB[0]; | ||
}; | ||
exports.isPRRelatedError = (fileChanges, error) => { | ||
if (error.code === 'MISSING_README') { | ||
return fileChanges.some(item => exports.hasCommonRPFolder(item.path, error.folderUrl)); | ||
} | ||
return false; | ||
}; | ||
/** | ||
@@ -23,0 +35,0 @@ * If the function is called in Azure DevOps CI for a Pull Request, it creates a |
import * as jsonParser from '@ts-common/json-parser'; | ||
declare type ErrorMessage = 'The example JSON file is not referenced from the swagger file.' | 'The swagger JSON file is not referenced from the readme file.' | 'The `readme.md` is not an AutoRest markdown file.' | 'The JSON file is not found but it is referenced from the readme file.' | 'The JSON file has a circular reference.' | 'The file is not a valid JSON file.'; | ||
declare type ErrorMessage = 'The example JSON file is not referenced from the swagger file.' | 'The swagger JSON file is not referenced from the readme file.' | 'The `readme.md` is not an AutoRest markdown file.' | 'The JSON file is not found but it is referenced from the readme file.' | 'The JSON file has a circular reference.' | 'The file is not a valid JSON file.' | 'Can not find readme.md in the folder. If no readme.md file, it will block SDK generation.'; | ||
export interface IErrorBase { | ||
@@ -56,4 +56,9 @@ readonly level: 'Warning' | 'Error'; | ||
} & IErrorBase; | ||
export declare type Error = JsonParseError | FileError | NotAutoRestMarkDown; | ||
export declare type MissingReadmeError = { | ||
readonly code: 'MISSING_README'; | ||
readonly message: ErrorMessage; | ||
readonly folderUrl: string; | ||
} & IErrorBase; | ||
export declare type Error = JsonParseError | FileError | NotAutoRestMarkDown | MissingReadmeError; | ||
export {}; | ||
//# sourceMappingURL=errors.d.ts.map |
@@ -47,2 +47,8 @@ "use strict"; | ||
} | ||
case 'MISSING_README': { | ||
return { | ||
code: error.code, | ||
url: error.folderUrl, | ||
}; | ||
} | ||
} | ||
@@ -114,3 +120,46 @@ }; | ||
const isExample = (filePath) => filePath.split(path.sep).some(name => name === 'examples'); | ||
const containsReadme = async (folder) => { | ||
const readmePath = path.resolve(folder, 'readme.md'); | ||
return fs.exists(readmePath); | ||
}; | ||
/** | ||
* Validate each RP folder must have its readme file. | ||
* | ||
* @param specification specification folder | ||
*/ | ||
const validateRPFolderMustContainReadme = (specification) => asyncIt.iterable(async function* () { | ||
const validDirs = ['data-plane', 'resource-manager']; | ||
const ignoredDirs = ['common']; | ||
const allJsonDir = fs | ||
.recursiveReaddir(specification) | ||
.filter(filePath => path.extname(filePath) === '.json' && | ||
validDirs.some(item => filePath.includes(item) && !ignoredDirs.some(ignoredItem => filePath.toLowerCase().includes(ignoredItem)))) | ||
.map(filepath => path.dirname(filepath)); | ||
const allJsonSet = new Set(); | ||
for await (const dir of allJsonDir) { | ||
if (allJsonSet.has(dir)) { | ||
continue; | ||
} | ||
allJsonSet.add(dir); | ||
// tslint:disable-next-line: no-let | ||
let curDir = dir; | ||
// tslint:disable-next-line: no-let | ||
let found = false; | ||
while (curDir !== specification) { | ||
if (await containsReadme(curDir)) { | ||
found = true; | ||
} | ||
curDir = path.dirname(curDir); | ||
} | ||
if (!found) { | ||
yield { | ||
level: 'Error', | ||
code: 'MISSING_README', | ||
message: 'Can not find readme.md in the folder. If no readme.md file, it will block SDK generation.', | ||
folderUrl: dir, | ||
}; | ||
} | ||
} | ||
}); | ||
/** | ||
* The function will validate file reference as a directed graph and will detect circular reference. | ||
@@ -254,2 +303,3 @@ * Detect circular reference in a directed graph using colors. | ||
.filter(f => path.basename(f).toLowerCase() === 'readme.md'); | ||
yield* validateRPFolderMustContainReadme(specification); | ||
yield* allReadMeFiles.flatMap(validateReadMeFile); | ||
@@ -293,5 +343,11 @@ const referencedFiles = await allReadMeFiles | ||
const sourceMap = await avocadoForDir(pr.workingDir); | ||
const fileChanges = await pr.diff(); | ||
// remove existing errors. | ||
/* Note: For MISSING_README error if the error is related to the PR changes, | ||
avocado will directly report it even though it's not a new involved error in the pull request.*/ | ||
for (const e of targetMap.keys()) { | ||
sourceMap.delete(e); | ||
const error = sourceMap.get(e); | ||
if (error !== undefined && !devOps.isPRRelatedError(fileChanges, error)) { | ||
sourceMap.delete(e); | ||
} | ||
} | ||
@@ -298,0 +354,0 @@ yield* sourceMap.values(); |
{ | ||
"name": "@azure/avocado", | ||
"version": "0.6.2", | ||
"version": "0.6.3", | ||
"description": "A validator of OpenAPI configurations", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -22,2 +22,3 @@ # Avocado | ||
- Validate whether `swagger file` has a circular reference and report a warning. For more detail, see [CIRCULAR REFERENCE](#circular-reference) | ||
- Validate whether each RP folder contains readme file for SDK generation. | ||
@@ -45,2 +46,32 @@ ## How to use | ||
### JSON_PARSE | ||
Level: ERROR | ||
To solve json parse error, you need make sure the json format is valid. | ||
### NO_JSON_FILE_FOUND | ||
Level: ERROR | ||
Readme file references a non-existing json file. To solve the error you need to check whether the json file is existing. | ||
### UNREFERENCED_JSON_FILE | ||
Level: ERROR | ||
Json file must be referenced by the readme input file section or other json files. Eg, example swagger file should be referenced by main swagger json and for SDK generation main swagger should be referenced by the readme input file section. To solve the error you need to place the non-referenced file to proper place. | ||
### MISSING_README | ||
Level: ERROR | ||
Each resource provider folder must have a readme file which is required by downstream SDK generation. To solve the error, you need create a readme file contains SDK generation config. | ||
### NOT_AUTOREST_MARKDOWN | ||
Level: ERROR | ||
Each readme in resource provider folder should follow autorest markdown format. To solve the error, you need check the readme block quote whether contains `see https://aka.ms/autorest` literally. | ||
### CIRCULAR REFERENCE | ||
@@ -47,0 +78,0 @@ |
@@ -7,2 +7,3 @@ // Copyright (c) Microsoft Corporation. All rights reserved. | ||
import * as path from 'path' | ||
import * as err from './errors' | ||
import * as fs from '@ts-common/fs' | ||
@@ -73,2 +74,16 @@ import * as asyncIt from '@ts-common/async-iterator' | ||
export const hasCommonRPFolder = (pathA: string, pathB: string) => { | ||
const regex = new RegExp(/specification\/(\w)+\//) | ||
const matchA = pathA.match(regex) | ||
const matchB = pathB.match(regex) | ||
return matchA !== null && matchB !== null && matchA[0] === matchB[0] | ||
} | ||
export const isPRRelatedError = (fileChanges: readonly FileChange[], error: err.Error): boolean => { | ||
if (error.code === 'MISSING_README') { | ||
return fileChanges.some(item => hasCommonRPFolder(item.path, error.folderUrl)) | ||
} | ||
return false | ||
} | ||
/** | ||
@@ -75,0 +90,0 @@ * If the function is called in Azure DevOps CI for a Pull Request, it creates a |
@@ -13,2 +13,3 @@ // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| 'The file is not a valid JSON file.' | ||
| 'Can not find readme.md in the folder. If no readme.md file, it will block SDK generation.' | ||
@@ -72,2 +73,8 @@ export interface IErrorBase { | ||
export type Error = JsonParseError | FileError | NotAutoRestMarkDown | ||
export type MissingReadmeError = { | ||
readonly code: 'MISSING_README' | ||
readonly message: ErrorMessage | ||
readonly folderUrl: string | ||
} & IErrorBase | ||
export type Error = JsonParseError | FileError | NotAutoRestMarkDown | MissingReadmeError |
@@ -46,2 +46,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. | ||
} | ||
case 'MISSING_README': { | ||
return { | ||
code: error.code, | ||
url: error.folderUrl, | ||
} | ||
} | ||
} | ||
@@ -152,3 +158,56 @@ } | ||
const containsReadme = async (folder: string): Promise<boolean> => { | ||
const readmePath = path.resolve(folder, 'readme.md') | ||
return fs.exists(readmePath) | ||
} | ||
/** | ||
* Validate each RP folder must have its readme file. | ||
* | ||
* @param specification specification folder | ||
*/ | ||
const validateRPFolderMustContainReadme = (specification: string): asyncIt.AsyncIterableEx<err.Error> => | ||
asyncIt.iterable<err.Error>(async function*() { | ||
const validDirs: ReadonlyArray<string> = ['data-plane', 'resource-manager'] | ||
const ignoredDirs: ReadonlyArray<string> = ['common'] | ||
const allJsonDir = fs | ||
.recursiveReaddir(specification) | ||
.filter( | ||
filePath => | ||
path.extname(filePath) === '.json' && | ||
validDirs.some( | ||
item => | ||
filePath.includes(item) && !ignoredDirs.some(ignoredItem => filePath.toLowerCase().includes(ignoredItem)), | ||
), | ||
) | ||
.map(filepath => path.dirname(filepath)) | ||
const allJsonSet = new Set<string>() | ||
for await (const dir of allJsonDir) { | ||
if (allJsonSet.has(dir)) { | ||
continue | ||
} | ||
allJsonSet.add(dir) | ||
// tslint:disable-next-line: no-let | ||
let curDir = dir | ||
// tslint:disable-next-line: no-let | ||
let found = false | ||
while (curDir !== specification) { | ||
if (await containsReadme(curDir)) { | ||
found = true | ||
} | ||
curDir = path.dirname(curDir) | ||
} | ||
if (!found) { | ||
yield { | ||
level: 'Error', | ||
code: 'MISSING_README', | ||
message: 'Can not find readme.md in the folder. If no readme.md file, it will block SDK generation.', | ||
folderUrl: dir, | ||
} | ||
} | ||
} | ||
}) | ||
/** | ||
* The function will validate file reference as a directed graph and will detect circular reference. | ||
@@ -319,2 +378,4 @@ * Detect circular reference in a directed graph using colors. | ||
yield* validateRPFolderMustContainReadme(specification) | ||
yield* allReadMeFiles.flatMap(validateReadMeFile) | ||
@@ -366,5 +427,12 @@ | ||
const fileChanges = await pr.diff() | ||
// remove existing errors. | ||
/* Note: For MISSING_README error if the error is related to the PR changes, | ||
avocado will directly report it even though it's not a new involved error in the pull request.*/ | ||
for (const e of targetMap.keys()) { | ||
sourceMap.delete(e) | ||
const error = sourceMap.get(e) | ||
if (error !== undefined && !devOps.isPRRelatedError(fileChanges, error)) { | ||
sourceMap.delete(e) | ||
} | ||
} | ||
@@ -371,0 +439,0 @@ yield* sourceMap.values() |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
86720
1492
120