diff --git a/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts b/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts index 8a17fe082a..d7433da2d6 100644 --- a/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts +++ b/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts @@ -174,20 +174,23 @@ describe('backend-plugin-manager', () => { meta: { name: 'Error', message: expect.stringContaining( - `Cannot find module '${url.fileURLToPath( - location, - )}/dist/index.cjs.js' from `, + `Cannot find module '${path.resolve( + url.fileURLToPath(location), + 'dist/index.cjs.js', + )}' from `, ), _originalMessage: expect.stringContaining( - `Cannot find module '${url.fileURLToPath( - location, - )}/dist/index.cjs.js' from `, + `Cannot find module '${path.resolve( + url.fileURLToPath(location), + 'dist/index.cjs.js', + )}' from `, ), code: 'MODULE_NOT_FOUND', hint: '', - moduleName: `${url.fileURLToPath( - location, - )}/dist/index.cjs.js`, + moduleName: `${path.resolve( + url.fileURLToPath(location), + 'dist/index.cjs.js', + )}`, siblingWithSimilarExtensionFound: false, requireStack: undefined, }, @@ -351,7 +354,9 @@ describe('backend-plugin-manager', () => { }, ])('$name', async (tc: TestCase): Promise => { const plugin: ScannedPluginPackage = { - location: new URL(`file:///node_modules/jest-tests/${randomUUID()}`), + location: url.pathToFileURL( + path.resolve(`/node_modules/jest-tests/${randomUUID()}`), + ), manifest: tc.packageManifest, }; @@ -462,8 +467,8 @@ describe('backend-plugin-manager', () => { .spyOn(PluginScanner.prototype, 'scanRoot') .mockImplementation(async () => [ { - location: new URL( - 'file:///somewhere/dynamic-plugins-root/a-dynamic-plugin', + location: url.pathToFileURL( + path.resolve('/somewhere/dynamic-plugins-root/a-dynamic-plugin'), ), manifest: { name: 'test', @@ -528,10 +533,12 @@ describe('backend-plugin-manager', () => { expect(scanRootSpier).toHaveBeenCalled(); expect(mockedModuleLoader.bootstrap).toHaveBeenCalledWith( findPaths(__dirname).targetRoot, - ['/somewhere-else/a-dynamic-plugin'], + [path.resolve('/somewhere-else/a-dynamic-plugin')], ); expect(mockedModuleLoader.load).toHaveBeenCalledWith( - '/somewhere/dynamic-plugins-root/a-dynamic-plugin/dist/index.cjs.js', + path.resolve( + '/somewhere/dynamic-plugins-root/a-dynamic-plugin/dist/index.cjs.js', + ), ); }); }); diff --git a/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts b/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts index 5ced3cfbab..e346bf749f 100644 --- a/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts +++ b/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts @@ -15,7 +15,7 @@ */ import { PluginScanner } from './plugin-scanner'; -import { Logs, MockedLogger } from '../__testUtils__/testUtils'; +import { LogContent, Logs, MockedLogger } from '../__testUtils__/testUtils'; import { Config, ConfigReader } from '@backstage/config'; import path, { join } from 'path'; import { ScannedPluginPackage } from './types'; @@ -126,10 +126,16 @@ describe('plugin-scanner', () => { expect(logger.logs).toEqual({ infos: [ { - message: `rootDirectory changed (addDir - ${backstageRootDirectory}/first-dir/test-backend-plugin): scanning plugins again`, + message: `rootDirectory changed (addDir - ${path.resolve( + backstageRootDirectory, + 'first-dir/test-backend-plugin', + )}): scanning plugins again`, }, { - message: `rootDirectory changed (add - ${backstageRootDirectory}/first-dir/test-backend-plugin/package.json): scanning plugins again`, + message: `rootDirectory changed (add - ${path.resolve( + backstageRootDirectory, + 'first-dir/test-backend-plugin/package.json', + )}): scanning plugins again`, }, ], }); @@ -151,7 +157,10 @@ describe('plugin-scanner', () => { expect(logger.logs).toEqual({ infos: [ { - message: `rootDirectory changed in Config from '${backstageRootDirectory}/first-dir' to '${backstageRootDirectory}/second-dir'`, + message: `rootDirectory changed in Config from '${path.resolve( + backstageRootDirectory, + 'first-dir', + )}' to '${path.resolve(backstageRootDirectory, 'second-dir')}'`, }, ], }); @@ -204,10 +213,16 @@ describe('plugin-scanner', () => { expect(logger.logs).toEqual({ infos: [ { - message: `rootDirectory changed (addDir - ${backstageRootDirectory}/second-dir/second-test-backend-plugin): scanning plugins again`, + message: `rootDirectory changed (addDir - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin', + )}): scanning plugins again`, }, { - message: `rootDirectory changed (add - ${backstageRootDirectory}/second-dir/second-test-backend-plugin/package.json): scanning plugins again`, + message: `rootDirectory changed (add - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/package.json', + )}): scanning plugins again`, }, ], }); @@ -263,22 +278,41 @@ describe('plugin-scanner', () => { expect(debug).toHaveBeenCalledTimes(4); }); - expect(logger.logs).toEqual({ - debugs: [ - { - message: `rootDirectory changed (addDir - ${backstageRootDirectory}/second-dir/second-test-backend-plugin/sub-directory): no need to scan plugins again`, - }, - { - message: `rootDirectory changed (add - ${backstageRootDirectory}/second-dir/second-test-backend-plugin/not-package.json): no need to scan plugins again`, - }, - { - message: `rootDirectory changed (unlink - ${backstageRootDirectory}/second-dir/second-test-backend-plugin/not-package.json): no need to scan plugins again`, - }, - { - message: `rootDirectory changed (unlinkDir - ${backstageRootDirectory}/second-dir/second-test-backend-plugin/sub-directory): no need to scan plugins again`, - }, - ], - }); + const onWindows = path.sep === '\\'; + // Order of events is not fixed on Windows. + // Windows sometimes even adds a 'change' event when a file is unlinked. + // So let's not try to tes the detail of received events on Windows + if (!onWindows) { + // eslint-disable-next-line + expect(logger.logs).toEqual({ + debugs: [ + { + message: `rootDirectory changed (addDir - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/sub-directory', + )}): no need to scan plugins again`, + }, + { + message: `rootDirectory changed (add - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/not-package.json', + )}): no need to scan plugins again`, + }, + { + message: `rootDirectory changed (unlink - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/not-package.json', + )}): no need to scan plugins again`, + }, + { + message: `rootDirectory changed (unlinkDir - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/sub-directory', + )}): no need to scan plugins again`, + }, + ], + }); + } logger.logs = {}; // Now check that removal of some plugin home directory triggers a new scan of plugins @@ -299,26 +333,39 @@ describe('plugin-scanner', () => { expect(scannedPlugins).toEqual([]); }); - expect(logger.logs).toEqual({ - infos: [ + if (!onWindows) { + // eslint-disable-next-line + expect(logger.logs.infos).toEqual([ { - message: `rootDirectory changed (unlink - ${backstageRootDirectory}/second-dir/second-test-backend-plugin/package.json): scanning plugins again`, + message: `rootDirectory changed (unlink - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/package.json', + )}): scanning plugins again`, }, - ], - errors: [ - { - message: `failed to load dynamic plugin manifest from '${backstageRootDirectory}/second-dir/second-test-backend-plugin'`, - meta: { - code: 'ENOENT', - errno: -2, - message: `ENOENT: no such file or directory, open '${backstageRootDirectory}/second-dir/second-test-backend-plugin/package.json'`, - name: 'Error', - path: `${backstageRootDirectory}/second-dir/second-test-backend-plugin/package.json`, - syscall: 'open', - }, + ]); + } + expect(logger.logs.errors).toEqual([ + { + message: `failed to load dynamic plugin manifest from '${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin', + )}'`, + meta: { + code: 'ENOENT', + errno: path.sep === '\\' ? -4058 : -2, + message: `ENOENT: no such file or directory, open '${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/package.json', + )}'`, + name: 'Error', + path: `${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin/package.json', + )}`, + syscall: 'open', }, - ], - }); + }, + ]); logger.logs = {}; await rm( @@ -334,13 +381,19 @@ describe('plugin-scanner', () => { }); rootDirectorySubscriber.mockClear(); - expect(logger.logs).toEqual({ - infos: [ - { - message: `rootDirectory changed (unlinkDir - ${backstageRootDirectory}/second-dir/second-test-backend-plugin): scanning plugins again`, - }, - ], - }); + if (!onWindows) { + // eslint-disable-next-line + expect(logger.logs).toEqual({ + infos: [ + { + message: `rootDirectory changed (unlinkDir - ${path.resolve( + backstageRootDirectory, + 'second-dir/second-test-backend-plugin', + )}): scanning plugins again`, + }, + ], + }); + } logger.logs = {}; getOptional.mockReturnValue({ @@ -353,8 +406,16 @@ describe('plugin-scanner', () => { { message: 'failed to apply new config for dynamic plugins', meta: { - message: `Dynamic plugins under '/somewhere-else/second-dir' cannot access backstage modules in '${backstageRootDirectory}/node_modules'. -Please add '${backstageRootDirectory}/node_modules' to the 'NODE_PATH' when running the backstage backend.`, + message: `Dynamic plugins under '${path.resolve( + '/somewhere-else/second-dir', + )}' cannot access backstage modules in '${path.resolve( + backstageRootDirectory, + 'node_modules', + )}'. +Please add '${path.resolve( + backstageRootDirectory, + 'node_modules', + )}' to the 'NODE_PATH' when running the backstage backend.`, name: 'Error', }, }, diff --git a/packages/backend-plugin-manager/src/scanner/plugin-scanner.test.ts b/packages/backend-plugin-manager/src/scanner/plugin-scanner.test.ts index 25f07baeaa..563cd9eb68 100644 --- a/packages/backend-plugin-manager/src/scanner/plugin-scanner.test.ts +++ b/packages/backend-plugin-manager/src/scanner/plugin-scanner.test.ts @@ -20,6 +20,7 @@ import { JsonObject } from '@backstage/types'; import { Logs, MockedLogger } from '../__testUtils__/testUtils'; import { ConfigReader } from '@backstage/config'; import path from 'path'; +import * as url from 'url'; import { ScannedPluginPackage } from './types'; describe('plugin-scanner', () => { @@ -73,7 +74,7 @@ describe('plugin-scanner', () => { rootDirectory: 'dist-dynamic', }, }, - expectedRootDirectory: '/backstageRoot/dist-dynamic', + expectedRootDirectory: path.resolve('/backstageRoot/dist-dynamic'), }, { name: 'valid config with absolute root directory path inside the backstage root', @@ -90,7 +91,7 @@ describe('plugin-scanner', () => { rootDirectory: '/backstageRoot/dist-dynamic', }, }, - expectedRootDirectory: '/backstageRoot/dist-dynamic', + expectedRootDirectory: path.resolve('/backstageRoot/dist-dynamic'), }, { name: 'valid config with absolute root directory path outside the backstage root', @@ -107,8 +108,14 @@ describe('plugin-scanner', () => { rootDirectory: '/somewhere/dist-dynamic', }, }, - expectedError: `Dynamic plugins under '/somewhere/dist-dynamic' cannot access backstage modules in '/backstageRoot/node_modules'. -Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the backstage backend.`, + expectedError: `Dynamic plugins under '${path.resolve( + '/somewhere/dist-dynamic', + )}' cannot access backstage modules in '${path.resolve( + '/backstageRoot/node_modules', + )}'. +Please add '${path.resolve( + '/backstageRoot/node_modules', + )}' to the 'NODE_PATH' when running the backstage backend.`, }, { name: 'valid config with absolute root directory path outside the backstage root but with backstage root included in NODE_PATH', @@ -126,9 +133,13 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac }, }, environment: { - NODE_PATH: `/somewhere-else${path.delimiter}/backstageRoot/node_modules${path.delimiter}/anywhere-else`, + NODE_PATH: `${path.resolve('/somewhere-else')}${ + path.delimiter + }${path.resolve('/backstageRoot', 'node_modules')}${ + path.delimiter + }${path.resolve('anywhere-else')}`, }, - expectedRootDirectory: '/somewhere/dist-dynamic', + expectedRootDirectory: path.resolve('/somewhere/dist-dynamic'), }, { name: 'invalid config: dynamicPlugins not an object', @@ -273,8 +284,8 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac }, expectedPluginPackages: [ { - location: new URL( - 'file:///backstageRoot/dist-dynamic/test-backend-plugin', + location: url.pathToFileURL( + path.resolve('/backstageRoot/dist-dynamic/test-backend-plugin'), ), manifest: { name: 'test-backend-plugin-dynamic', @@ -318,8 +329,8 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac }, expectedPluginPackages: [ { - location: new URL( - 'file:///backstageRoot/dist-dynamic/test-backend-plugin', + location: url.pathToFileURL( + path.resolve('/backstageRoot/dist-dynamic/test-backend-plugin'), ), manifest: { name: 'test-backend-plugin-dynamic', @@ -347,8 +358,9 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac expectedLogs: { infos: [ { - message: - "skipping '/backstageRoot/dist-dynamic/test-backend-plugin' since it is not a directory", + message: `skipping '${path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin', + )}' since it is not a directory`, }, ], }, @@ -377,8 +389,9 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac expectedLogs: { infos: [ { - message: - "skipping '/backstageRoot/dist-dynamic/test-backend-plugin' since it is not a directory", + message: `skipping '${path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin', + )}' since it is not a directory`, }, ], }, @@ -421,8 +434,8 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac }, expectedPluginPackages: [ { - location: new URL( - 'file:///backstageRoot/dist-dynamic/test-backend-plugin', + location: url.pathToFileURL( + path.resolve('/backstageRoot/dist-dynamic/test-backend-plugin'), ), manifest: { name: 'test-backend-plugin-dynamic', @@ -471,8 +484,10 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac }, expectedPluginPackages: [ { - location: new URL( - 'file:///backstageRoot/dist-dynamic/test-backend-plugin/alpha', + location: url.pathToFileURL( + path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin/alpha', + ), ), manifest: { name: 'test-backend-plugin-dynamic', @@ -511,8 +526,8 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac }, expectedPluginPackages: [ { - location: new URL( - 'file:///backstageRoot/dist-dynamic/test-backend-plugin', + location: url.pathToFileURL( + path.resolve('/backstageRoot/dist-dynamic/test-backend-plugin'), ), manifest: { name: 'test-backend-plugin-dynamic', @@ -525,8 +540,9 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac expectedLogs: { warns: [ { - message: - "skipping '/backstageRoot/dist-dynamic/test-backend-plugin/alpha' since it is not a directory", + message: `skipping '${path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin/alpha', + )}' since it is not a directory`, }, ], }, @@ -567,8 +583,9 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac expectedLogs: { errors: [ { - message: - "failed to load dynamic plugin manifest from '/backstageRoot/dist-dynamic/test-backend-plugin/alpha'", + message: `failed to load dynamic plugin manifest from '${path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin/alpha', + )}'`, meta: { name: 'SyntaxError', message: 'Unexpected token i in JSON at position 0', @@ -600,8 +617,9 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac expectedLogs: { errors: [ { - message: - "failed to load dynamic plugin manifest from '/backstageRoot/dist-dynamic/test-backend-plugin'", + message: `failed to load dynamic plugin manifest from '${path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin', + )}'`, meta: { name: 'SyntaxError', message: 'Unexpected token i in JSON at position 0', @@ -637,8 +655,9 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac expectedLogs: { errors: [ { - message: - "failed to load dynamic plugin manifest from '/backstageRoot/dist-dynamic/test-backend-plugin'", + message: `failed to load dynamic plugin manifest from '${path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin', + )}'`, meta: { name: 'Error', message: "field 'backstage.role' not found in 'package.json'", @@ -674,8 +693,9 @@ Please add '/backstageRoot/node_modules' to the 'NODE_PATH' when running the bac expectedLogs: { errors: [ { - message: - "failed to load dynamic plugin manifest from '/backstageRoot/dist-dynamic/test-backend-plugin'", + message: `failed to load dynamic plugin manifest from '${path.resolve( + '/backstageRoot/dist-dynamic/test-backend-plugin', + )}'`, meta: { name: 'Error', message: "field 'main' not found in 'package.json'", diff --git a/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts b/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts index bdcf786619..e226a1a8ad 100644 --- a/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts +++ b/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts @@ -84,13 +84,13 @@ export class PluginScanner { } const dynamicPluginsRootPath = path.isAbsolute(dynamicPlugins.rootDirectory) - ? dynamicPlugins.rootDirectory + ? path.resolve(dynamicPlugins.rootDirectory) : path.resolve(this.backstageRoot, dynamicPlugins.rootDirectory); if ( !path - .dirname(path.normalize(dynamicPluginsRootPath)) - .startsWith(path.normalize(this.backstageRoot)) + .dirname(dynamicPluginsRootPath) + .startsWith(path.resolve(this.backstageRoot)) ) { const nodePath = process.env.NODE_PATH; const backstageNodeModules = path.resolve(