From 2fc91bd78f957d4ddb51f315ed1b1b2213216786 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 21 Aug 2024 13:25:29 +0200 Subject: [PATCH 1/6] repo-tools: Add backend-lint command Signed-off-by: Johan Haals --- packages/repo-tools/package.json | 1 + .../src/commands/backend-lint/backend-lint.ts | 75 +++++++++++++++++++ packages/repo-tools/src/commands/index.ts | 9 +++ yarn.lock | 25 ++++++- 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 packages/repo-tools/src/commands/backend-lint/backend-lint.ts diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index f337329537..77d0047e60 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -73,6 +73,7 @@ "minimatch": "^9.0.0", "p-limit": "^3.0.2", "portfinder": "^1.0.32", + "ts-morph": "^23.0.0", "yaml-diff-patch": "^2.0.0" }, "devDependencies": { diff --git a/packages/repo-tools/src/commands/backend-lint/backend-lint.ts b/packages/repo-tools/src/commands/backend-lint/backend-lint.ts new file mode 100644 index 0000000000..2f7aa3cc04 --- /dev/null +++ b/packages/repo-tools/src/commands/backend-lint/backend-lint.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2024 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. + */ +import fs from 'fs-extra'; +import { Project } from 'ts-morph'; +import { resolvePackagePaths } from '../../lib/paths'; + +const project = new Project({ + tsConfigFilePath: 'tsconfig.json', +}); + +export async function lint(paths: string[]) { + const pkgs = (await resolvePackagePaths()).filter(pkg => { + const role = JSON.parse(fs.readFileSync(`${pkg}/package.json`, 'utf-8')) + .backstage?.role; + return role === 'backend-plugin' || role === 'backend-plugin-module'; + }); + + if (paths.length > 0) { + paths.forEach(verifyIndex); + return; + } + pkgs.forEach(verifyIndex); +} + +function verifyIndex(pkg: string) { + console.log(`Verifying ${pkg}`); + const sourceFile = project.getSourceFile(`${pkg}/src/index.ts`); + if (!sourceFile) { + console.log(`Could not find ${pkg}/src/index.ts`); + process.exit(1); + } + const symbols = sourceFile?.getExportSymbols(); + + const exportCount = symbols?.length || 0; + if (exportCount > 1) { + console.log( + ` ⚠️ Warning: ${exportCount} exports found, ${symbols + .map(symbol => symbol.getName()) + .join(', ')}`, + ); + } + + const createRouterExport = symbols?.find( + symbol => symbol.getName() === 'createRouter', + ); + + if (!sourceFile.getDefaultExportSymbol()) { + console.log(' ❌ Missing default export'); + } + let createRouterDeprecated = undefined; + if (createRouterExport) { + createRouterDeprecated = createRouterExport + .getJsDocTags() + .find(tag => tag.getName() === 'deprecated'); + } + + if (createRouterExport) { + console.log(' ❌ createRouter is exported'); + if (!createRouterDeprecated) + console.log(' ❌ createRouter is NOT deprecated'); + } +} diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 24ec2af411..7527edc870 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -236,6 +236,15 @@ export function registerCommands(program: Command) { ), ); + program + .command('backend-lint [paths...]') + .description( + 'Lint backend plugin packages for errors related to the new backend system migration', + ) + .action( + lazy(() => import('./backend-lint/backend-lint').then(m => m.lint)), + ); + registerPackageCommand(program); registerRepoCommand(program); } diff --git a/yarn.lock b/yarn.lock index 6c6d884fec..b0e460ee11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7889,6 +7889,7 @@ __metadata: minimatch: ^9.0.0 p-limit: ^3.0.2 portfinder: ^1.0.32 + ts-morph: ^23.0.0 yaml-diff-patch: ^2.0.0 peerDependencies: "@microsoft/api-extractor-model": "*" @@ -17092,6 +17093,18 @@ __metadata: languageName: node linkType: hard +"@ts-morph/common@npm:~0.24.0": + version: 0.24.0 + resolution: "@ts-morph/common@npm:0.24.0" + dependencies: + fast-glob: ^3.3.2 + minimatch: ^9.0.4 + mkdirp: ^3.0.1 + path-browserify: ^1.0.1 + checksum: 793bc8a47c93ab55c6c036f94480d3b0e948661aef4bb7dbc29279b1dda2fc4fce809a88e221537867a313541842e12d1ecbd32b4769688abe1303807ec09db6 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.9 resolution: "@tsconfig/node10@npm:1.0.9" @@ -34350,7 +34363,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3": +"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4": version: 9.0.5 resolution: "minimatch@npm:9.0.5" dependencies: @@ -42884,6 +42897,16 @@ __metadata: languageName: node linkType: hard +"ts-morph@npm:^23.0.0": + version: 23.0.0 + resolution: "ts-morph@npm:23.0.0" + dependencies: + "@ts-morph/common": ~0.24.0 + code-block-writer: ^13.0.1 + checksum: 3282eb0f8bd4577770874736c3259b97501da9a86137160b5d68f106b7848ea7b1fbccf9e198a3d930ec40c993e9951d4bfae31e2562dac8f3de0d7bb0e23615 + languageName: node + linkType: hard + "ts-node@npm:^10.9.1": version: 10.9.2 resolution: "ts-node@npm:10.9.2" From 2379dd5d855f36d3370856e010da20a695085dc5 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 21 Aug 2024 14:32:51 +0200 Subject: [PATCH 2/6] chore: update cli report Signed-off-by: Johan Haals --- packages/repo-tools/cli-report.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index a06f426baf..47a7d6d01f 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -16,6 +16,7 @@ Commands: type-deps generate-catalog-info [options] knip-reports [options] [paths...] + backend-lint [paths...] package [command] repo [command] help [command] @@ -39,6 +40,15 @@ Options: -h, --help ``` +### `backstage-repo-tools backend-lint` + +``` +Usage: backstage-repo-tools backend-lint [options] [paths...] + +Options: + -h, --help +``` + ### `backstage-repo-tools generate-catalog-info` ``` From 8ec374aaf678605e2ef0856f41a0e97734e40893 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 21 Aug 2024 15:08:39 +0200 Subject: [PATCH 3/6] feat(repo-tools): check for backend commons or tasks dependencies Co-authored-by: Johan Haals Signed-off-by: Camila Belo --- .../src/commands/backend-lint/backend-lint.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/repo-tools/src/commands/backend-lint/backend-lint.ts b/packages/repo-tools/src/commands/backend-lint/backend-lint.ts index 2f7aa3cc04..a9218b5b85 100644 --- a/packages/repo-tools/src/commands/backend-lint/backend-lint.ts +++ b/packages/repo-tools/src/commands/backend-lint/backend-lint.ts @@ -21,10 +21,13 @@ const project = new Project({ tsConfigFilePath: 'tsconfig.json', }); +function readPackageJson(pkg: string) { + return JSON.parse(fs.readFileSync(`${pkg}/package.json`, 'utf-8')); +} + export async function lint(paths: string[]) { const pkgs = (await resolvePackagePaths()).filter(pkg => { - const role = JSON.parse(fs.readFileSync(`${pkg}/package.json`, 'utf-8')) - .backstage?.role; + const role = readPackageJson(pkg).backstage?.role; return role === 'backend-plugin' || role === 'backend-plugin-module'; }); @@ -72,4 +75,19 @@ function verifyIndex(pkg: string) { if (!createRouterDeprecated) console.log(' ❌ createRouter is NOT deprecated'); } + + const pkgJson = readPackageJson(pkg); + if ( + '@backstage/backend-common' in pkgJson.dependencies || + '@backstage/backend-common' in pkgJson.devDependencies + ) { + console.log(' ❌ Stop depending on "@backstage/backend-common"'); + } + + if ( + '@backstage/backend-tasks' in pkgJson.dependencies || + '@backstage/backend-tasks' in pkgJson.devDependencies + ) { + console.log(' ❌ Stop depending on "@backstage/backend-tasks"'); + } } From ef4b339da1dfcfd5243a68b95c3d4ba99261e9c6 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 22 Aug 2024 09:56:40 +0200 Subject: [PATCH 4/6] chore: Use existing utilities for listing packages Signed-off-by: Johan Haals --- .../src/commands/backend-lint/backend-lint.ts | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/repo-tools/src/commands/backend-lint/backend-lint.ts b/packages/repo-tools/src/commands/backend-lint/backend-lint.ts index a9218b5b85..6025defe31 100644 --- a/packages/repo-tools/src/commands/backend-lint/backend-lint.ts +++ b/packages/repo-tools/src/commands/backend-lint/backend-lint.ts @@ -13,36 +13,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import fs from 'fs-extra'; import { Project } from 'ts-morph'; -import { resolvePackagePaths } from '../../lib/paths'; +import { BackstagePackageJson, PackageGraph } from '@backstage/cli-node'; +import fs from 'fs-extra'; +import { paths as cliPaths } from '../../lib/paths'; +import path from 'path'; const project = new Project({ - tsConfigFilePath: 'tsconfig.json', + tsConfigFilePath: cliPaths.resolveTargetRoot('tsconfig.json'), }); function readPackageJson(pkg: string) { - return JSON.parse(fs.readFileSync(`${pkg}/package.json`, 'utf-8')); + return JSON.parse(fs.readFileSync(path.join(pkg, 'package.json'), 'utf-8')); } export async function lint(paths: string[]) { - const pkgs = (await resolvePackagePaths()).filter(pkg => { - const role = readPackageJson(pkg).backstage?.role; - return role === 'backend-plugin' || role === 'backend-plugin-module'; + const pkgs = (await PackageGraph.listTargetPackages()).filter(pkg => { + return ( + pkg.packageJson.backstage?.role === 'backend-plugin' || + pkg.packageJson.backstage?.role === 'backend-plugin-module' + ); }); if (paths.length > 0) { - paths.forEach(verifyIndex); + paths.forEach(pkg => verifyIndex(pkg)); return; } - pkgs.forEach(verifyIndex); + pkgs.forEach(pkg => verifyIndex(pkg.dir, pkg.packageJson)); } -function verifyIndex(pkg: string) { +function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) { console.log(`Verifying ${pkg}`); - const sourceFile = project.getSourceFile(`${pkg}/src/index.ts`); + const tsPath = path.join(pkg, 'src/index.ts'); + const sourceFile = project.getSourceFile(tsPath); if (!sourceFile) { - console.log(`Could not find ${pkg}/src/index.ts`); + console.log(`Could not find ${tsPath}`); process.exit(1); } const symbols = sourceFile?.getExportSymbols(); @@ -76,18 +81,18 @@ function verifyIndex(pkg: string) { console.log(' ❌ createRouter is NOT deprecated'); } - const pkgJson = readPackageJson(pkg); + const pkgJson = packageJson ?? readPackageJson(pkg); if ( '@backstage/backend-common' in pkgJson.dependencies || '@backstage/backend-common' in pkgJson.devDependencies ) { - console.log(' ❌ Stop depending on "@backstage/backend-common"'); + console.log(' ❌ Stop depending on @backstage/backend-common'); } if ( '@backstage/backend-tasks' in pkgJson.dependencies || '@backstage/backend-tasks' in pkgJson.devDependencies ) { - console.log(' ❌ Stop depending on "@backstage/backend-tasks"'); + console.log(' ❌ Stop depending on @backstage/backend-tasks'); } } From 5b8d33f9c6af7cb87d21dd7adef5ad53eb598394 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 22 Aug 2024 15:25:49 +0200 Subject: [PATCH 5/6] Rename command to "lint legacy-backend-exports" Signed-off-by: Johan Haals --- packages/repo-tools/src/commands/index.ts | 27 ++++++++++++------- .../lint-legacy-backend-exports.ts} | 0 2 files changed, 18 insertions(+), 9 deletions(-) rename packages/repo-tools/src/commands/{backend-lint/backend-lint.ts => lint-legacy-backend-exports/lint-legacy-backend-exports.ts} (100%) diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 7527edc870..5116326eea 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -162,6 +162,23 @@ function registerRepoCommand(program: Command) { ); } +function registerLintCommand(program: Command) { + const lintCommand = program + .command('lint [command]') + .description('Tools for linting repository.'); + lintCommand + .command('legacy-backend-exports [paths...]') + .description( + 'Lint backend plugin packages for legacy exports and make sure it conforms to the new export pattern', + ) + .action( + lazy(() => + import( + './lint-legacy-backend-exports/lint-legacy-backend-exports' + ).then(m => m.lint), + ), + ); +} export function registerCommands(program: Command) { program .command('api-reports [paths...]') @@ -236,17 +253,9 @@ export function registerCommands(program: Command) { ), ); - program - .command('backend-lint [paths...]') - .description( - 'Lint backend plugin packages for errors related to the new backend system migration', - ) - .action( - lazy(() => import('./backend-lint/backend-lint').then(m => m.lint)), - ); - registerPackageCommand(program); registerRepoCommand(program); + registerLintCommand(program); } // Wraps an action function so that it always exits and handles errors diff --git a/packages/repo-tools/src/commands/backend-lint/backend-lint.ts b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts similarity index 100% rename from packages/repo-tools/src/commands/backend-lint/backend-lint.ts rename to packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts From e7f4952989f19f67ae2cb84c89126c68e6fa543a Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 22 Aug 2024 17:38:12 +0200 Subject: [PATCH 6/6] docs: update api reports Signed-off-by: Camila Belo --- packages/repo-tools/cli-report.md | 33 +++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 47a7d6d01f..6e0e4c9b85 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -16,9 +16,9 @@ Commands: type-deps generate-catalog-info [options] knip-reports [options] [paths...] - backend-lint [paths...] package [command] repo [command] + lint [command] help [command] ``` @@ -40,15 +40,6 @@ Options: -h, --help ``` -### `backstage-repo-tools backend-lint` - -``` -Usage: backstage-repo-tools backend-lint [options] [paths...] - -Options: - -h, --help -``` - ### `backstage-repo-tools generate-catalog-info` ``` @@ -70,6 +61,28 @@ Options: -h, --help ``` +### `backstage-repo-tools lint` + +``` +Usage: backstage-repo-tools lint [options] [command] [command] + +Options: + -h, --help + +Commands: + legacy-backend-exports [paths...] + help [command] +``` + +### `backstage-repo-tools lint legacy-backend-exports` + +``` +Usage: backstage-repo-tools lint legacy-backend-exports [options] [paths...] + +Options: + -h, --help +``` + ### `backstage-repo-tools package` ```