Add CLI command for full repo check.
Signed-off-by: Aramis Sennyey <sennyeya@amazon.com>
This commit is contained in:
committed by
Fredrik Adelöw
parent
abd4ff4cee
commit
6a985600a7
@@ -106,6 +106,7 @@
|
||||
"node-libs-browser": "^2.2.1",
|
||||
"npm-packlist": "^5.0.0",
|
||||
"ora": "^5.3.0",
|
||||
"p-limit": "^3.0.2",
|
||||
"postcss": "^8.1.0",
|
||||
"process": "^0.11.10",
|
||||
"react-dev-utils": "^12.0.0-next.60",
|
||||
|
||||
@@ -87,6 +87,13 @@ export function registerRepoCommand(program: Command) {
|
||||
)
|
||||
.action(lazy(() => import('./openapi/verify').then(m => m.bulkCommand)));
|
||||
|
||||
command
|
||||
.command('schema:openapi:generate')
|
||||
.description(
|
||||
'Generates a Typescript file from an OpenAPI yaml spec. For use with the `@backstage/backend-openapi-utils` ApiRouter type.',
|
||||
)
|
||||
.action(lazy(() => import('./openapi/generate').then(m => m.bulkCommand)));
|
||||
|
||||
command
|
||||
.command('test')
|
||||
.allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args
|
||||
|
||||
@@ -17,25 +17,81 @@
|
||||
import fs from 'fs-extra';
|
||||
import { paths } from '../../lib/paths';
|
||||
import YAML from 'js-yaml';
|
||||
import chalk from 'chalk';
|
||||
import { resolve } from 'path';
|
||||
import { PackageGraph } from '../../lib/monorepo';
|
||||
import pLimit from 'p-limit';
|
||||
import { relative as relativePath } from 'path';
|
||||
|
||||
async function generate(
|
||||
directoryPath: string,
|
||||
config?: { skipMissingYamlFile: boolean },
|
||||
) {
|
||||
const { skipMissingYamlFile } = config ?? {};
|
||||
const openapiPath = resolve(directoryPath, 'openapi.yaml');
|
||||
if (!(await fs.pathExists(openapiPath))) {
|
||||
if (skipMissingYamlFile) {
|
||||
return;
|
||||
}
|
||||
throw new Error('Could not find openapi.yaml in root of directory.');
|
||||
}
|
||||
const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8'));
|
||||
|
||||
// For now, we're not adding a header or linting after pasting.
|
||||
await fs.writeFile(
|
||||
resolve(directoryPath, 'schema/openapi.ts'),
|
||||
`export default ${JSON.stringify(yaml, null, 2)} as const`,
|
||||
);
|
||||
}
|
||||
|
||||
export async function command() {
|
||||
const openapiPath = paths.resolveTarget('openapi.yaml');
|
||||
if (!(await fs.pathExists(openapiPath))) {
|
||||
console.warn('Could not find openapi.yaml in root of directory.');
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
const yaml = YAML.load(
|
||||
await fs.readFile(paths.resolveTarget('openapi.yaml'), 'utf8'),
|
||||
);
|
||||
|
||||
// For now, we're not adding a header or linting after pasting.
|
||||
await fs.writeFile(
|
||||
paths.resolveTarget('schema/openapi.ts'),
|
||||
`export default ${JSON.stringify(yaml, null, 2)} as const`,
|
||||
);
|
||||
await generate(paths.resolveTarget('.'));
|
||||
console.log(chalk.green('OpenAPI files successfully generated.'));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error(chalk.red(err.message));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export async function bulkCommand(): Promise<void> {
|
||||
const packages = await PackageGraph.listTargetPackages();
|
||||
const limit = pLimit(5);
|
||||
|
||||
const resultsList = await Promise.all(
|
||||
packages.map(pkg =>
|
||||
limit(async () => {
|
||||
let resultText = '';
|
||||
try {
|
||||
await generate(pkg.dir, { skipMissingYamlFile: true });
|
||||
} catch (err) {
|
||||
resultText = err.message;
|
||||
}
|
||||
|
||||
return {
|
||||
relativeDir: relativePath(paths.targetRoot, pkg.dir),
|
||||
resultText,
|
||||
};
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
let failed = false;
|
||||
for (const { relativeDir, resultText } of resultsList) {
|
||||
if (resultText) {
|
||||
console.log();
|
||||
console.log(
|
||||
chalk.red(
|
||||
`OpenAPI yaml to Typescript generation failed in ${relativeDir}:`,
|
||||
),
|
||||
);
|
||||
console.log(resultText.trimStart());
|
||||
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import { PackageGraph } from '../../lib/monorepo';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import Parser from '@apidevtools/swagger-parser';
|
||||
import { detectRoleFromPackage } from '../../lib/role';
|
||||
import pLimit from 'p-limit';
|
||||
|
||||
const SUPPORTED_ROLES = [
|
||||
'backend',
|
||||
@@ -35,14 +36,15 @@ const SUPPORTED_ROLES = [
|
||||
|
||||
async function verify(
|
||||
directoryPath: string,
|
||||
{ checkRole }: { checkRole: boolean },
|
||||
config: { checkRole: boolean } = { checkRole: false },
|
||||
) {
|
||||
const { checkRole } = config ?? {};
|
||||
if (checkRole) {
|
||||
const role = detectRoleFromPackage(
|
||||
await fs.readJson(resolve(directoryPath, 'package.json')),
|
||||
);
|
||||
|
||||
if (!SUPPORTED_ROLES.some(r => r === role)) {
|
||||
if (!role || !SUPPORTED_ROLES.includes(role)) {
|
||||
console.log(chalk.red(`Unsupported role ${role}`));
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -61,15 +63,17 @@ async function verify(
|
||||
throw new Error('No `schema/openapi.ts` file found.');
|
||||
}
|
||||
const schema = await import(join(directoryPath, 'schema/openapi'));
|
||||
if (schema.default) {
|
||||
if (!isEqual(schema.default, yaml)) {
|
||||
throw new Error(
|
||||
'`openapi.yaml` and `schema/openapi.ts` do not match. Please run `yarn build:openapi` to generate the `schema/openapi.ts` file from the `openapi.yaml` file.',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!schema.default) {
|
||||
throw new Error('`schemas/openapi.ts` needs to have a default export.');
|
||||
}
|
||||
if (!isEqual(schema.default, yaml)) {
|
||||
throw new Error(
|
||||
`\`openapi.yaml\` and \`schema/openapi.ts\` do not match. Please run \`yarn --cwd ${relativePath(
|
||||
paths.targetRoot,
|
||||
directoryPath,
|
||||
)} schema:openapi:generate\` to regenerate \`schema/openapi.ts\`.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function command() {
|
||||
@@ -84,28 +88,31 @@ export async function command() {
|
||||
|
||||
export async function bulkCommand(): Promise<void> {
|
||||
const packages = await PackageGraph.listTargetPackages();
|
||||
const limit = pLimit(5);
|
||||
|
||||
const resultsList = await Promise.all(
|
||||
packages.map(async pkg => {
|
||||
let resultText = '';
|
||||
try {
|
||||
await verify(pkg.dir, { checkRole: false });
|
||||
} catch (err) {
|
||||
resultText = err.message;
|
||||
}
|
||||
packages.map(pkg =>
|
||||
limit(async () => {
|
||||
let resultText = '';
|
||||
try {
|
||||
await verify(pkg.dir);
|
||||
} catch (err) {
|
||||
resultText = err.message;
|
||||
}
|
||||
|
||||
return {
|
||||
relativeDir: relativePath(paths.targetRoot, pkg.dir),
|
||||
resultText,
|
||||
};
|
||||
}),
|
||||
return {
|
||||
relativeDir: relativePath(paths.targetRoot, pkg.dir),
|
||||
resultText,
|
||||
};
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
let failed = false;
|
||||
for (const { relativeDir, resultText } of resultsList) {
|
||||
if (resultText) {
|
||||
console.log();
|
||||
console.log(chalk.red(`Lint failed in ${relativeDir}:`));
|
||||
console.log(chalk.red(`OpenAPI validation failed in ${relativeDir}:`));
|
||||
console.log(resultText.trimStart());
|
||||
|
||||
failed = true;
|
||||
|
||||
@@ -112,7 +112,7 @@ export default {
|
||||
required: false,
|
||||
schema: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
minimum: 0,
|
||||
},
|
||||
},
|
||||
limit: {
|
||||
|
||||
Reference in New Issue
Block a user