diff --git a/workspaces/jenkins/.changeset/kind-crabs-enjoy.md b/workspaces/jenkins/.changeset/kind-crabs-enjoy.md new file mode 100644 index 0000000000..7d92b51a71 --- /dev/null +++ b/workspaces/jenkins/.changeset/kind-crabs-enjoy.md @@ -0,0 +1,6 @@ +--- +'@backstage-community/plugin-jenkins-backend': major +'@backstage-community/plugin-jenkins': major +--- + +Drops support for old backend system which includes removing exports for JenkinsBuilder. Please migrate to the new backend system way of installing the plugin. diff --git a/workspaces/jenkins/bcp.json b/workspaces/jenkins/bcp.json index 9d2158e4fb..e1b8e25f0c 100644 --- a/workspaces/jenkins/bcp.json +++ b/workspaces/jenkins/bcp.json @@ -1,4 +1,5 @@ { "autoVersionBump": true, - "knipReports": false + "knipReports": false, + "listDeprecations": true } diff --git a/workspaces/jenkins/packages/app-next/src/App.test.tsx b/workspaces/jenkins/packages/app-next/src/App.test.tsx index 1474311714..94d38d7980 100644 --- a/workspaces/jenkins/packages/app-next/src/App.test.tsx +++ b/workspaces/jenkins/packages/app-next/src/App.test.tsx @@ -38,7 +38,7 @@ describe('App', () => { }; const { default: app } = await import('./App'); - const rendered = await renderWithEffects(app); + const rendered = await renderWithEffects(app.createRoot()); expect(rendered.baseElement).toBeInTheDocument(); }); }); diff --git a/workspaces/jenkins/packages/app-next/src/App.tsx b/workspaces/jenkins/packages/app-next/src/App.tsx index 9e4a2572cf..63983c17c4 100644 --- a/workspaces/jenkins/packages/app-next/src/App.tsx +++ b/workspaces/jenkins/packages/app-next/src/App.tsx @@ -64,7 +64,7 @@ const scmIntegrationsApi = ApiBlueprint.make({ }), }); -export const app = createApp({ +export default createApp({ features: [ catalogPlugin, catalogImportPlugin, @@ -77,5 +77,3 @@ export const app = createApp({ }), ], }); - -export default app.createRoot(); diff --git a/workspaces/jenkins/packages/app-next/src/index.tsx b/workspaces/jenkins/packages/app-next/src/index.tsx index ead4d6ba18..fd31fa16ff 100644 --- a/workspaces/jenkins/packages/app-next/src/index.tsx +++ b/workspaces/jenkins/packages/app-next/src/index.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import '@backstage/cli/asset-types'; -import ReactDOM from 'react-dom'; -import app from './App'; +import ReactDOM from 'react-dom/client'; +import App from './App'; -ReactDOM.render(app, document.getElementById('root')); +ReactDOM.createRoot(document.getElementById('root')!).render(App.createRoot()); diff --git a/workspaces/jenkins/plugins/jenkins-backend/report.api.md b/workspaces/jenkins/plugins/jenkins-backend/report.api.md index f2984b5d8f..5cbf23214d 100644 --- a/workspaces/jenkins/plugins/jenkins-backend/report.api.md +++ b/workspaces/jenkins/plugins/jenkins-backend/report.api.md @@ -6,7 +6,7 @@ import { AuthService } from '@backstage/backend-plugin-api'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { BackstageCredentials } from '@backstage/backend-plugin-api'; -import { CatalogApi } from '@backstage/catalog-client'; +import { CatalogService } from '@backstage/plugin-catalog-node'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { DiscoveryService } from '@backstage/backend-plugin-api'; @@ -20,7 +20,7 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { // (undocumented) static fromConfig(options: { config: Config; - catalog: CatalogApi; + catalog: CatalogService; discovery: DiscoveryService; auth: AuthService; httpAuth?: HttpAuthService; diff --git a/workspaces/jenkins/plugins/jenkins-backend/src/plugin.ts b/workspaces/jenkins/plugins/jenkins-backend/src/plugin.ts index b3e651c546..039d5d37de 100644 --- a/workspaces/jenkins/plugins/jenkins-backend/src/plugin.ts +++ b/workspaces/jenkins/plugins/jenkins-backend/src/plugin.ts @@ -19,8 +19,9 @@ import { createBackendPlugin, } from '@backstage/backend-plugin-api'; import { DefaultJenkinsInfoProvider } from './service/jenkinsInfoProvider'; -import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node'; import { JenkinsBuilder } from './service/JenkinsBuilder'; +import { jenkinsPermissions } from '@backstage-community/plugin-jenkins-common'; /** * Jenkins backend plugin @@ -40,6 +41,7 @@ export const jenkinsPlugin = createBackendPlugin({ discovery: coreServices.discovery, auth: coreServices.auth, httpAuth: coreServices.httpAuth, + permissionsRegistry: coreServices.permissionsRegistry, }, async init({ logger, @@ -50,6 +52,7 @@ export const jenkinsPlugin = createBackendPlugin({ discovery, auth, httpAuth, + permissionsRegistry, }) { const jenkinsInfoProvider = DefaultJenkinsInfoProvider.fromConfig({ auth, @@ -76,6 +79,7 @@ export const jenkinsPlugin = createBackendPlugin({ }); const { router } = await builder.build(); httpRouter.use(router); + permissionsRegistry.addPermissions(jenkinsPermissions); }, }); }, diff --git a/workspaces/jenkins/plugins/jenkins-backend/src/service/JenkinsBuilder.ts b/workspaces/jenkins/plugins/jenkins-backend/src/service/JenkinsBuilder.ts index 2cd58c9e82..46c6ec2f66 100644 --- a/workspaces/jenkins/plugins/jenkins-backend/src/service/JenkinsBuilder.ts +++ b/workspaces/jenkins/plugins/jenkins-backend/src/service/JenkinsBuilder.ts @@ -32,8 +32,6 @@ import { } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; -import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; -import { jenkinsPermissions } from '@backstage-community/plugin-jenkins-common'; /** @public */ export type JenkinsBuilderReturn = Promise<{ @@ -110,11 +108,6 @@ export class JenkinsBuilder { const router = Router(); router.use(express.json()); - router.use( - createPermissionIntegrationRouter({ - permissions: jenkinsPermissions, - }), - ); router.get( '/v1/entity/:namespace/:kind/:name/projects', diff --git a/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts b/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts index f445111000..4ef7f87986 100644 --- a/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts +++ b/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts @@ -14,8 +14,12 @@ * limitations under the License. */ -import { CatalogApi } from '@backstage/catalog-client'; -import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; +import { CatalogService } from '@backstage/plugin-catalog-node'; +import { + Entity, + CompoundEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; import { DefaultJenkinsInfoProvider, @@ -171,9 +175,9 @@ describe('JenkinsConfig', () => { }); describe('DefaultJenkinsInfoProvider', () => { - const mockCatalog: jest.Mocked = { + const mockCatalog: jest.Mocked = { getEntityByRef: jest.fn(), - } as any as jest.Mocked; + } as any as jest.Mocked; const entityRef: CompoundEntityRef = { kind: 'Component', @@ -201,8 +205,14 @@ describe('DefaultJenkinsInfoProvider', () => { await expect(provider.getInstance({ entityRef })).rejects.toThrow(); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); }); @@ -229,8 +239,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toStrictEqual({ baseUrl: 'https://jenkins.example.com', @@ -270,8 +286,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -310,8 +332,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -350,8 +378,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins-other.example.com', @@ -379,8 +413,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -408,8 +448,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -438,8 +484,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -472,8 +524,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins-other.example.com', @@ -507,8 +565,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins-other.example.com', @@ -544,8 +608,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkinsOverriden.example.com', @@ -580,8 +650,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -615,8 +691,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', @@ -651,8 +733,14 @@ describe('DefaultJenkinsInfoProvider', () => { const info: JenkinsInfo = await provider.getInstance({ entityRef }); expect(mockCatalog.getEntityByRef).toHaveBeenCalledWith( - entityRef, - undefined, + stringifyEntityRef(entityRef), + expect.objectContaining({ + credentials: expect.objectContaining({ + principal: expect.objectContaining({ + type: 'service', + }), + }), + }), ); expect(info).toMatchObject({ baseUrl: 'https://jenkins.example.com', diff --git a/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts b/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts index 0d68ee6d57..6a624a8026 100644 --- a/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts +++ b/workspaces/jenkins/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts @@ -21,7 +21,7 @@ import { HttpAuthService, LoggerService, } from '@backstage/backend-plugin-api'; -import { CatalogApi } from '@backstage/catalog-client'; +import { CatalogService } from '@backstage/plugin-catalog-node'; import { Entity, CompoundEntityRef, @@ -203,14 +203,14 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { private constructor( private readonly config: JenkinsConfig, - private readonly catalog: CatalogApi, + private readonly catalog: CatalogService, private readonly auth: AuthService, private logger: LoggerService, ) {} static fromConfig(options: { config: Config; - catalog: CatalogApi; + catalog: CatalogService; discovery: DiscoveryService; auth: AuthService; httpAuth?: HttpAuthService; @@ -233,13 +233,11 @@ export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { const DEFAULT_LIMITATION_OF_PROJECTS = 50; // load entity + const credentials = + opt.credentials ?? (await this.auth.getOwnServiceCredentials()); const entity = await this.catalog.getEntityByRef( - opt.entityRef, - opt.credentials && - (await this.auth.getPluginRequestToken({ - onBehalfOf: opt.credentials, - targetPluginId: 'catalog', - })), + stringifyEntityRef(opt.entityRef), + { credentials }, ); if (!entity) { throw new Error( diff --git a/workspaces/jenkins/plugins/jenkins/src/components/BuildsPage/lib/CITable/columns.tsx b/workspaces/jenkins/plugins/jenkins/src/components/BuildsPage/lib/CITable/columns.tsx index 1a9229437c..0f06676fae 100644 --- a/workspaces/jenkins/plugins/jenkins/src/components/BuildsPage/lib/CITable/columns.tsx +++ b/workspaces/jenkins/plugins/jenkins/src/components/BuildsPage/lib/CITable/columns.tsx @@ -23,7 +23,7 @@ import Typography from '@material-ui/core/Typography'; import RetryIcon from '@material-ui/icons/Replay'; import VisibilityIcon from '@material-ui/icons/Visibility'; import HistoryIcon from '@material-ui/icons/History'; -import { useState } from 'react'; +import { useState, JSX } from 'react'; import { Project } from '../../../../api/JenkinsApi'; import { buildRouteRef, jobRunsRouteRef } from '../../../../plugin'; import { JenkinsRunStatus } from '../Status';