From 4977f58b9a688fa812edd298f4dd9c2667482d00 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Tue, 27 Jan 2026 19:14:44 -0600 Subject: [PATCH] Refactored into a script using zod based on feedback Signed-off-by: Andre Wanlin --- .github/workflows/ci.yml | 20 ++----- docs/plugins/add-to-directory.md | 2 +- microsite/data/plugin-schema.json | 56 -------------------- package.json | 5 +- scripts/verify-plugin-directory.js | 84 ++++++++++++++++++++++++++++++ yarn.lock | 54 ++----------------- 6 files changed, 94 insertions(+), 127 deletions(-) delete mode 100644 microsite/data/plugin-schema.json create mode 100644 scripts/verify-plugin-directory.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43216d35f9..4ce5f8bc0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,8 @@ on: # NOTE: If you change these you must update ci-noop.yml as well pull_request: paths-ignore: - - 'microsite/**' - - 'beps/**' + - "microsite/**" + - "beps/**" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -121,20 +121,8 @@ jobs: - name: verify doc links run: node scripts/verify-links.js - - name: verify plugin directory files - run: | - if find ./microsite/data/plugins -maxdepth 1 -type f ! \( -name "*.yaml" \) | grep -q .; then - find ./microsite/data/plugins -maxdepth 1 -type f ! \( -name "*.yaml" \) - echo "Error: Non-YAML files found" - exit 1 - else - echo "Success: All files in are YAML files (based on extension)." - exit 0 - fi - - - name: verify plugin directory schema - run: yarn ajv validate -s microsite/data/plugin-schema.json -d "microsite/data/plugins/*.yaml" -c ajv-formats - + - name: verify plugin directory + run: node scripts/verify-plugin-directory.js - name: build all packages run: yarn backstage-cli repo build --all diff --git a/docs/plugins/add-to-directory.md b/docs/plugins/add-to-directory.md index aa5a9aba53..48affef489 100644 --- a/docs/plugins/add-to-directory.md +++ b/docs/plugins/add-to-directory.md @@ -28,7 +28,7 @@ addedDate: # The date plugin added to directory E.g. '2022-10-01' quotes are req You can validate your YAML file is correct by running the following from the root of the repo: 1. First run `yarn install` -2. Then run `yarn ajv validate -s microsite/data/plugin-schema.json -d "microsite/data/plugins/.yaml" -c ajv-formats` where `` is the name of your plugin +2. Then run `node ./scripts/verify-plugin-directory.js` If there are any errors they will be listed and you will need to correct them. We run this same check as part of the CI. diff --git a/microsite/data/plugin-schema.json b/microsite/data/plugin-schema.json deleted file mode 100644 index 55277736f4..0000000000 --- a/microsite/data/plugin-schema.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-06/schema#", - "$ref": "#/definitions/plugin", - "definitions": { - "plugin": { - "type": "object", - "additionalProperties": false, - "properties": { - "title": { - "type": "string" - }, - "author": { - "type": "string" - }, - "authorUrl": { - "type": "string", - "format": "uri" - }, - "category": { - "type": "string" - }, - "description": { - "type": "string" - }, - "documentation": { - "type": "string", - "format": "uri" - }, - "iconUrl": { - "type": "string" - }, - "npmPackageName": { - "type": "string" - }, - "addedDate": { - "type": "string", - "format": "date" - }, - "order": { - "type": "number" - } - }, - "required": [ - "addedDate", - "author", - "authorUrl", - "category", - "description", - "documentation", - "npmPackageName", - "title" - ], - "title": "plugin" - } - } -} diff --git a/package.json b/package.json index 48b0b20bfe..c47762b780 100644 --- a/package.json +++ b/package.json @@ -152,8 +152,6 @@ "@types/memjs": "^1.3.3", "@types/node": "^22.13.14", "@types/webpack": "^5.28.0", - "ajv-cli": "^5.0.0", - "ajv-formats": "^3.0.1", "array-to-table": "^1.0.1", "command-exists": "^1.2.9", "cross-env": "^10.0.0", @@ -181,7 +179,8 @@ "ts-morph": "^24.0.0", "typedoc": "^0.28.0", "typescript": "~5.7.0", - "vite": "^7.1.5" + "vite": "^7.1.5", + "zod": "^3.25.76" }, "packageManager": "yarn@4.8.1", "engines": { diff --git a/scripts/verify-plugin-directory.js b/scripts/verify-plugin-directory.js new file mode 100644 index 0000000000..532f19f5e6 --- /dev/null +++ b/scripts/verify-plugin-directory.js @@ -0,0 +1,84 @@ +#!/usr/bin/env node +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fs = require('fs-extra'); +const { resolve, join } = require('node:path'); +const yaml = require('js-yaml'); +const z = require('zod'); + +const configSchema = z.object({ + title: z.string(), + author: z.string(), + authorUrl: z.string().url(), + category: z.string(), + description: z.string(), + documentation: z.string().url(), + iconUrl: z.string().optional(), + npmPackageName: z.string(), + addedDate: z.coerce.date(), + order: z.number().optional(), +}); + +async function main() { + const rootPath = resolve(__dirname, '..'); + const pluginDirectoryPath = resolve(rootPath, 'microsite', 'data', 'plugins'); + + const pluginDirectoryFiles = await fs.readdir(pluginDirectoryPath); + + let hasErrors = false; + for (const pluginDirectoryFile of pluginDirectoryFiles) { + if (!pluginDirectoryFile.toLocaleLowerCase().includes('.yaml')) { + hasErrors = true; + console.log(`${pluginDirectoryFile} is missing the '.yaml' extension`); + } + + const pluginDirectoryFilePath = join( + pluginDirectoryPath, + pluginDirectoryFile, + ); + + const pluginDataYaml = yaml.load( + fs.readFileSync(pluginDirectoryFilePath, { encoding: 'utf-8' }), + ); + + try { + configSchema.parse(pluginDataYaml); + } catch (error) { + if (error instanceof z.ZodError) { + console.log( + `YAML data validation failed for ${pluginDirectoryFile}:`, + error.errors, + ); + } else { + console.log( + `An unexpected error occurred for ${pluginDirectoryFile}:`, + error, + ); + } + } + } + if (hasErrors) { + throw new Error( + 'There was an error verifying the Plugin Directory data files, review the output for guidance on what corrections are needed', + ); + } +} + +main(process.argv.slice(2)).catch(error => { + console.error(error.stack || error); + process.exit(1); +}); diff --git a/yarn.lock b/yarn.lock index 6b9485ee02..8b3e5957f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24306,28 +24306,6 @@ __metadata: languageName: node linkType: hard -"ajv-cli@npm:^5.0.0": - version: 5.0.0 - resolution: "ajv-cli@npm:5.0.0" - dependencies: - ajv: "npm:^8.0.0" - fast-json-patch: "npm:^2.0.0" - glob: "npm:^7.1.0" - js-yaml: "npm:^3.14.0" - json-schema-migrate: "npm:^2.0.0" - json5: "npm:^2.1.3" - minimist: "npm:^1.2.0" - peerDependencies: - ts-node: ">=9.0.0" - peerDependenciesMeta: - ts-node: - optional: true - bin: - ajv: dist/index.js - checksum: 10/bd755f7f34602356c5c65bf947f3ba8d73b31859296b275eb3b112d2c14e125fcfca71bac9c8d4242a006fc62e9c868be3cf4652ebc6a8df08fe16f83ee9080c - languageName: node - linkType: hard - "ajv-draft-04@npm:^1.0.0, ajv-draft-04@npm:~1.0.0": version: 1.0.0 resolution: "ajv-draft-04@npm:1.0.0" @@ -31196,13 +31174,6 @@ __metadata: languageName: node linkType: hard -"fast-deep-equal@npm:^2.0.1": - version: 2.0.1 - resolution: "fast-deep-equal@npm:2.0.1" - checksum: 10/b701835a87985e0ec4925bdf1f0c1e7eb56309b5d12d534d5b4b69d95a54d65bb16861c081781ead55f73f12d6c60ba668713391ee7fbf6b0567026f579b7b0b - languageName: node - linkType: hard - "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -31237,15 +31208,6 @@ __metadata: languageName: node linkType: hard -"fast-json-patch@npm:^2.0.0": - version: 2.2.1 - resolution: "fast-json-patch@npm:2.2.1" - dependencies: - fast-deep-equal: "npm:^2.0.1" - checksum: 10/802924e8f6e50267c1c92c9a52de59e9e4d448a383c32119e228a17ef17fe90e54f52bb4211a616a9c93e708f873cc39d5df10ae77fe32cd5a22c4b9cb69cae9 - languageName: node - linkType: hard - "fast-json-patch@npm:^3.0.0-1, fast-json-patch@npm:^3.1.0, fast-json-patch@npm:^3.1.1": version: 3.1.1 resolution: "fast-json-patch@npm:3.1.1" @@ -32642,7 +32604,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.0, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.3": +"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.3": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -36058,7 +36020,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.14.0, js-yaml@npm:^3.6.1, js-yaml@npm:^3.8.3": +"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1, js-yaml@npm:^3.8.3": version: 3.14.2 resolution: "js-yaml@npm:3.14.2" dependencies: @@ -36309,15 +36271,6 @@ __metadata: languageName: node linkType: hard -"json-schema-migrate@npm:^2.0.0": - version: 2.0.0 - resolution: "json-schema-migrate@npm:2.0.0" - dependencies: - ajv: "npm:^8.0.0" - checksum: 10/bb32718273e0a1bcce5f3926f5c93559ceb209af19abb5af441fc1af937cfba39bab9884ae5461d91c5ba6019ea80067226b7c7a5b073fd8ab53133a981c387f - languageName: node - linkType: hard - "json-schema-to-ts@npm:^3.0.0": version: 3.1.1 resolution: "json-schema-to-ts@npm:3.1.1" @@ -45561,8 +45514,6 @@ __metadata: "@types/node": "npm:^22.13.14" "@types/webpack": "npm:^5.28.0" "@useoptic/optic": "npm:^1.0.0" - ajv-cli: "npm:^5.0.0" - ajv-formats: "npm:^3.0.1" array-to-table: "npm:^1.0.1" command-exists: "npm:^1.2.9" cross-env: "npm:^10.0.0" @@ -45592,6 +45543,7 @@ __metadata: typescript: "npm:~5.7.0" vite: "npm:^7.1.5" yaml: "npm:^2.7.0" + zod: "npm:^3.25.76" languageName: unknown linkType: soft