diff --git a/.changeset/cold-crews-peel.md b/.changeset/cold-crews-peel.md new file mode 100644 index 0000000000..465982920e --- /dev/null +++ b/.changeset/cold-crews-peel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +Fixed dependency to frontend package from tests diff --git a/.changeset/mean-pillows-enter.md b/.changeset/mean-pillows-enter.md new file mode 100644 index 0000000000..500c9af486 --- /dev/null +++ b/.changeset/mean-pillows-enter.md @@ -0,0 +1,15 @@ +--- +'@backstage/eslint-plugin': patch +--- + +Added new eslint rule to restrict mixed plugin imports. + +New rule `@backstage/no-mixed-plugin-imports` disallows mixed imports between plugins that are mixing +the backstage architecture. This rule forces that: + +- No imports from frontend plugins to backend plugins or other frontend plugins. +- No imports from backend plugins to frontend plugins or other backend plugins. +- No imports from common plugins to frontend or backend plugins. + +The current recommended configuration is giving a warning for mixed imports. This is to be changed in +the future to an error so please adjust your workspace accordingly. diff --git a/.eslintrc.js b/.eslintrc.js index 409b820783..54f9e69576 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,8 +18,26 @@ var path = require('path'); module.exports = { root: true, - plugins: ['@spotify', 'notice', 'react', 'testing-library'], + plugins: ['@spotify', 'notice', 'react', 'testing-library', '@backstage'], rules: { + '@backstage/no-mixed-plugin-imports': [ + 'error', + { + // TODO: Fix these either by right role or by moving things to new packages + excludedTargetPackages: [ + '@backstage/plugin-catalog', + '@backstage/plugin-techdocs', + '@backstage/plugin-app', + '@backstage/plugin-catalog-backend', + '@backstage/test-utils', + '@backstage/plugin-auth-backend', + '@backstage/plugin-permission-backend', + '@backstage/plugin-kubernetes-backend', + '@backstage/config-loader', + '@backstage/plugin-app-backend' + ], + } + ], 'react/react-in-jsx-scope': 'off', 'notice/notice': [ 'error', diff --git a/package.json b/package.json index fc773cb0d0..0b827a0ce3 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ "@backstage/codemods": "workspace:*", "@backstage/create-app": "workspace:*", "@backstage/e2e-test-utils": "workspace:*", + "@backstage/eslint-plugin": "workspace:*", "@backstage/repo-tools": "workspace:*", "@changesets/cli": "^2.14.0", "@octokit/rest": "^19.0.3", diff --git a/packages/canon/.eslintrc.js b/packages/canon/.eslintrc.js index f7c0e8cbd1..9638ff6e45 100644 --- a/packages/canon/.eslintrc.js +++ b/packages/canon/.eslintrc.js @@ -3,6 +3,7 @@ module.exports = { extends: ['plugin:storybook/recommended'], rules: { 'react/forbid-elements': 'off', + '@backstage/no-mixed-plugin-imports': 'off' }, }; diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 600b3268e1..e30b9e5de3 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -41,3 +41,4 @@ The following rules are provided by this plugin: | [@backstage/no-relative-monorepo-imports](./docs/rules/no-relative-monorepo-imports.md) | Forbid relative imports that reach outside of the package in a monorepo. | | [@backstage/no-undeclared-imports](./docs/rules/no-undeclared-imports.md) | Forbid imports of external packages that have not been declared in the appropriate dependencies field in `package.json`. | | [@backstage/no-top-level-material-ui-4-imports](./docs/rules/no-top-level-material-ui-4-imports.md) | Forbid top level import from Material UI v4 packages. | +| [@backstage/no-mixed-plugin-imports](./docs/rules/no-mixed-plugin-imports.md) | Disallow mixed plugin imports. | diff --git a/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md b/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md new file mode 100644 index 0000000000..e8570054e4 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md @@ -0,0 +1,67 @@ +# @backstage/no-mixed-plugin-imports + +Disallow mixed imports between backstage plugins. + +## Usage + +Add the rules as follows, it has no options: + +```js +"@backstage/no-mixed-plugin-imports": ["error"] +``` + +## Rule Details + +Given the following two target packages: + +```json +{ + "name": "@backstage/plugin-foo", + "backstage": { + "role": "frontend-plugin" + } +} +``` + +```json +{ + "name": "@backstage/plugin-bar", + "backstage": { + "role": "frontend-plugin" + } +} +``` + +### Fail + +```ts +import { FooCard } from '@backstage/plugin-foo'; +``` + +### Pass + +```ts +import { FooCard } from '@backstage/plugin-foo-react'; +``` + +## Options + +You can ignore specific target packages or files by adding them to the options in the `.eslintrc.js` file: + +```js +{ + rules: { + '@backstage/no-mixed-plugin-imports': [ + 'error', + { + excludedTargetPackages: [ + '@backstage/plugin-foo', + ], + excludedFiles: [ + '**/*.{test,spec}.[jt]s?(x)' + ], + } + ] + } +} +``` diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js index af1a8891c3..58b597a438 100644 --- a/packages/eslint-plugin/index.js +++ b/packages/eslint-plugin/index.js @@ -22,6 +22,7 @@ module.exports = { '@backstage/no-forbidden-package-imports': 'error', '@backstage/no-relative-monorepo-imports': 'error', '@backstage/no-undeclared-imports': 'error', + '@backstage/no-mixed-plugin-imports': 'warn', }, }, }, @@ -30,5 +31,6 @@ module.exports = { 'no-relative-monorepo-imports': require('./rules/no-relative-monorepo-imports'), 'no-undeclared-imports': require('./rules/no-undeclared-imports'), 'no-top-level-material-ui-4-imports': require('./rules/no-top-level-material-ui-4-imports'), + 'no-mixed-plugin-imports': require('./rules/no-mixed-plugin-imports'), }, }; diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js index 3b46f41a54..e90536ff52 100644 --- a/packages/eslint-plugin/lib/getPackages.js +++ b/packages/eslint-plugin/lib/getPackages.js @@ -21,7 +21,7 @@ const manypkg = require('@manypkg/get-packages'); /** * @typedef ExtendedPackage - * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array, backstage?: { inline?: boolean } }}} packageJson + * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array, backstage?: { inline?: boolean, role?: string } }}} packageJson */ /** diff --git a/packages/eslint-plugin/rules/no-mixed-plugin-imports.js b/packages/eslint-plugin/rules/no-mixed-plugin-imports.js new file mode 100644 index 0000000000..b5e79b509e --- /dev/null +++ b/packages/eslint-plugin/rules/no-mixed-plugin-imports.js @@ -0,0 +1,249 @@ +/* + * Copyright 2023 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. + */ + +// @ts-check + +const visitImports = require('../lib/visitImports'); +const getPackages = require('../lib/getPackages'); +const minimatch = require('minimatch'); + +/** @typedef {import('../lib/getPackages.js').ExtendedPackage} ExtendedPackage */ + +/** + * @param {string} pattern + * @param {string} filePath + * @returns {boolean} + */ +const matchesPattern = (pattern, filePath) => { + return new minimatch.Minimatch(pattern).match(filePath); +}; + +const roleRules = [ + { + sourceRole: ['frontend-plugin', 'web-library'], + targetRole: [ + 'backend-plugin', + 'node-library', + 'backend-plugin-module', + 'frontend-plugin', + ], + }, + { + sourceRole: ['backend-plugin', 'node-library', 'backend-plugin-module'], + targetRole: ['frontend-plugin', 'web-library', 'backend-plugin'], + }, + { + sourceRole: ['common-library'], + targetRole: [ + 'frontend-plugin', + 'web-library', + 'backend-plugin', + 'node-library', + 'backend-plugin-module', + ], + }, +]; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + messages: { + forbidden: + '{{sourcePackage}} ({{sourceRole}}) uses forbidden import from {{targetPackage}} ({{targetRole}}).', + useReactPlugin: + 'Use web library {{targetPackage}}-react or common library instead.', + useNodePlugin: + 'Use node library {{targetPackage}}-node or common library instead.', + useCommonPlugin: 'Use common library {{targetPackage}}-common instead.', + removeImport: + 'Remove this import to avoid mixed plugin imports. Fix the code by refactoring it to use the correct plugin type.', + }, + docs: { + description: 'Disallow mixed plugin imports.', + url: 'https://github.com/backstage/backstage/blob/master/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md', + }, + hasSuggestions: true, + schema: [ + { + type: 'object', + properties: { + excludedTargetPackages: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + excludedFiles: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + includedFiles: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + }, + additionalProperties: false, + }, + ], + }, + create(context) { + const packages = getPackages(context.cwd); + if (!packages) { + return {}; + } + + const filePath = context.physicalFilename + ? context.physicalFilename + : context.filename; + + /** @type {ExtendedPackage | undefined} */ + const pkg = packages.byPath(filePath); + if (!pkg) { + return {}; + } + + const options = context.options[0] || {}; + /** @type {string[]} */ + const ignoreTargetPackages = options.excludedTargetPackages || []; + /** @type {string[]} */ + const excludePatterns = options.excludedFiles || []; + /** @type {string[]} */ + const includePatterns = options.includedFiles || ['**/src/**']; + + if ( + !includePatterns.some(pattern => matchesPattern(pattern, filePath)) || + excludePatterns.some(pattern => matchesPattern(pattern, filePath)) + ) { + return {}; + } + + return visitImports(context, (node, imp) => { + if (imp.type !== 'internal') { + return; + } + + /** @type {ExtendedPackage | undefined} */ + const targetPackage = imp.package; + const targetName = targetPackage?.packageJson.name; + const sourceName = pkg.packageJson.name; + if (sourceName === targetName) { + return; + } + + const sourceRole = pkg.packageJson.backstage?.role; + const targetRole = targetPackage.packageJson.backstage?.role; + if (!sourceRole || !targetRole) { + return; + } + + if ( + roleRules.some( + rule => + rule.sourceRole.includes(sourceRole) && + rule.targetRole.includes(targetRole) && + !ignoreTargetPackages.includes(targetName), + ) + ) { + const suggest = []; + + if ( + (sourceRole === 'frontend-plugin' || sourceRole === 'web-library') && + targetRole === 'frontend-plugin' + ) { + suggest.push({ + messageId: 'useReactPlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/'$/, "-react'"); + return fixer.replaceText(imp.node, newImport); + }, + }); + suggest.push({ + messageId: 'useCommonPlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/'$/, "-common'"); + return fixer.replaceText(imp.node, newImport); + }, + }); + } else if ( + (sourceRole === 'backend-plugin' || + sourceRole === 'backend-plugin-module') && + targetRole === 'backend-plugin' + ) { + suggest.push({ + messageId: 'useNodePlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/-backend'$/, "-node'"); + return fixer.replaceText(imp.node, newImport); + }, + }); + suggest.push({ + messageId: 'useCommonPlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/-backend'$/, '-common'); + return fixer.replaceText(imp.node, newImport); + }, + }); + } else { + suggest.push({ + messageId: 'removeImport', + /** @param {import('eslint').Rule.RuleFixer} _fixer */ + fix(_fixer) { + // Not a fixable case, just give a suggestion to remove the import + }, + }); + } + + context.report({ + node: node, + messageId: 'forbidden', + data: { + sourcePackage: pkg.packageJson.name || imp.package.dir, + sourceRole, + targetPackage: targetPackage.packageJson.name || imp.package.dir, + targetRole, + }, + suggest, + }); + } + }); + }, +}; diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json index 563ec35120..e8e4d4ba70 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json @@ -1,5 +1,12 @@ { "name": "@internal/foo", + "backstage": { + "role": "frontend-plugin" + }, + "files": [ + "dist", + "type-utils" + ], "dependencies": { "@internal/bar": "1.0.0" }, @@ -8,9 +15,5 @@ }, "peerDependencies": { "react": "*" - }, - "files": [ - "dist", - "type-utils" - ] + } } diff --git a/packages/eslint-plugin/src/no-mixed-plugin-imports.test.ts b/packages/eslint-plugin/src/no-mixed-plugin-imports.test.ts new file mode 100644 index 0000000000..45764ca71a --- /dev/null +++ b/packages/eslint-plugin/src/no-mixed-plugin-imports.test.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2023 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 { RuleTester } from 'eslint'; +import path from 'path'; +import rule from '../rules/no-mixed-plugin-imports'; + +const RULE = 'no-mixed-plugin-imports'; +const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo'); + +const ERR = ( + sourcePackage: string, + sourceRole: string, + targetPackage: string, + targetRole: string, +) => ({ + message: `${sourcePackage} (${sourceRole}) uses forbidden import from ${targetPackage} (${targetRole}).`, +}); + +// cwd must be restored +const origDir = process.cwd(); +afterAll(() => { + process.chdir(origDir); +}); +process.chdir(FIXTURE); + +const ruleTester = new RuleTester({ + parserOptions: { + sourceType: 'module', + ecmaVersion: 2021, + }, +}); + +ruleTester.run(RULE, rule, { + valid: [ + { + code: `import '@internal/inline'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + }, + ], + invalid: [ + { + code: `import '@internal/foo'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR( + '@internal/bar', + 'frontend-plugin', + '@internal/foo', + 'frontend-plugin', + ), + ], + }, + ], +}); diff --git a/packages/eslint-plugin/src/no-undeclared-imports.test.ts b/packages/eslint-plugin/src/no-undeclared-imports.test.ts index ee56c34926..7127d2f344 100644 --- a/packages/eslint-plugin/src/no-undeclared-imports.test.ts +++ b/packages/eslint-plugin/src/no-undeclared-imports.test.ts @@ -24,6 +24,7 @@ jest.mock('child_process', () => ({ const RULE = 'no-undeclared-imports'; const FIXTURE = joinPath(__dirname, '__fixtures__/monorepo'); +// const ERR_UNDECLARED = ( name: string, @@ -246,26 +247,14 @@ ruleTester.run(RULE, rule, { }, { code: `import 'react-dom'`, - output: `import 'directive:add-import:dependencies:react-dom'`, + output: `import 'directive:add-import:peerDependencies:react-dom'`, filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'), errors: [ ERR_UNDECLARED( 'react-dom', - 'dependencies', + 'peerDependencies', joinPath('packages', 'foo'), - ), - ], - }, - { - code: `import 'react-dom'`, - output: `import 'directive:add-import:devDependencies:react-dom'`, - filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'), - errors: [ - ERR_UNDECLARED( - 'react-dom', - 'devDependencies', - joinPath('packages', 'foo'), - '--dev', + '--peer', ), ], }, diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 4a451ce6fa..0d822bbd63 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -59,7 +59,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/core-app-api": "workspace:^", "@backstage/plugin-scaffolder-node-test-utils": "workspace:^" } } diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts index 4d8e0fd0c0..449c5980a4 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import yaml from 'yaml'; import { createGitlabGroupEnsureExistsAction } from './gitlabGroupEnsureExists'; import { examples } from './gitlabGroupEnsureExists.examples'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Groups: { @@ -50,15 +50,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -91,15 +93,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -138,15 +142,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2/group3', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -170,15 +176,17 @@ describe('gitlab:group:ensureExists', () => { }); it(`Should ${examples[3].description}`, async () => { - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -213,15 +221,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2/group3', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -264,15 +274,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2/group3/group4', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts index 3d703eb91f..8e52ee999e 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createGitlabGroupEnsureExistsAction } from './gitlabGroupEnsureExists'; import { getClient } from '../util'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Groups: { @@ -45,15 +45,17 @@ describe('gitlab:group:ensureExists', () => { jest.clearAllMocks(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts index 917ba61d91..858adb4b99 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createGitlabIssueAction } from './gitlabIssueCreate'; import { examples } from './gitlabIssueCreate.examples'; import yaml from 'yaml'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -46,15 +46,17 @@ describe('gitlab:issues:create', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'sample-token', - apiBaseUrl: 'https://gitlab.com/api/v1', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'sample-token', + apiBaseUrl: 'https://gitlab.com/api/v1', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts index d5aa2d1e17..e09bfaf249 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { IssueType } from '../commonGitlabConfig'; import { createGitlabIssueAction } from './gitlabIssueCreate'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -45,15 +45,17 @@ describe('gitlab:issues:create', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'myIntegrationsToken', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'myIntegrationsToken', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts index 7e50eac66c..fa53609c19 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts @@ -15,11 +15,11 @@ */ import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { editGitlabIssueAction } from './gitlabIssueEdit'; import { examples } from './gitlabIssueEdit.examples'; import yaml from 'yaml'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -46,15 +46,17 @@ describe('gitlab:issue:edit', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'myIntegrationsToken', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'myIntegrationsToken', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts index 9604fe8fe2..a16236a509 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts @@ -15,10 +15,10 @@ */ import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { IssueType } from '../commonGitlabConfig'; import { editGitlabIssueAction } from './gitlabIssueEdit'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -45,15 +45,17 @@ describe('gitlab:issue:edit', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'myIntegrationsToken', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'myIntegrationsToken', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts index 53cbfb0b87..41535479fe 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createTriggerGitlabPipelineAction } from './gitlabPipelineTrigger'; import { examples } from './gitlabPipelineTrigger.examples'; import yaml from 'yaml'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { PipelineTriggerTokens: { @@ -49,15 +49,17 @@ describe('gitlab:pipeline:trigger', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'glpat-abcdef', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'glpat-abcdef', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts index 3803198ec8..89498c52f2 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createTriggerGitlabPipelineAction } from './gitlabPipelineTrigger'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { PipelineTriggerTokens: { @@ -47,15 +47,17 @@ describe('gitlab:pipeline:trigger', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'glpat-abcdef', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'glpat-abcdef', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/yarn.lock b/yarn.lock index 6bc788890f..c63b0b49cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4602,7 +4602,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/eslint-plugin@workspace:^, @backstage/eslint-plugin@workspace:packages/eslint-plugin": +"@backstage/eslint-plugin@workspace:*, @backstage/eslint-plugin@workspace:^, @backstage/eslint-plugin@workspace:packages/eslint-plugin": version: 0.0.0-use.local resolution: "@backstage/eslint-plugin@workspace:packages/eslint-plugin" dependencies: @@ -7706,7 +7706,6 @@ __metadata: "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" - "@backstage/core-app-api": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" @@ -45453,6 +45452,7 @@ __metadata: "@backstage/create-app": "workspace:*" "@backstage/e2e-test-utils": "workspace:*" "@backstage/errors": "workspace:^" + "@backstage/eslint-plugin": "workspace:*" "@backstage/repo-tools": "workspace:*" "@changesets/cli": "npm:^2.14.0" "@manypkg/get-packages": "npm:^1.1.3"