This topic teaches you how to create a Tanzu Developer Portal plug-in by wrapping an existing Backstage plug-in. After you create a Tanzu Developer Portal plug-in, you can build a customized Tanzu Developer Portal with Configurator.
Meet the following prerequisites before creating a Tanzu Developer Portal plug-in.
Ensure that you have the following software installed locally to develop a Tanzu Developer Portal plug-in:
- Node 16:
nvm
is recommended. For how to installnvm
, see thenvm
GitHub repository. For how to install a specific version ofnvm
, see the NodeJS documentation. - yarn v1
- (Optional) A UNIX-based OS: If you use Windows, you must find alternatives to some commands in this topic.
Ensure that the Backstage plug-in you want to wrap is in an npm registry. You can use your own private registry or a public registry, such as npm JS. Both your development machine and your Tanzu Application Platform cluster must have access to the registry.
In this topic you will be wrapping the Backstage TechInsights plug-in as an example. This plug-in consists of back-end and front-end components, both of which are available on npm JS:
This topic tells you how to create two Tanzu Developer Portal plug-ins by wrapping the
@backstage/plugin-tech-insights
and @backstage/plugin-tech-insights-backend
Backstage plug-ins. You can create a separate
repository for each of these plug-ins, but it's easier and simpler to do the work for both in a single monorepo.
This topic describes how to use the Backstage tools @backstage/create-app
and backstage-cli
to
manage your monorepo. The Backstage tools make managing packages easier. However, you will
not develop a traditional Backstage app, and you will remove some portions of generated code later.
-
Run the
create-app
script and, when prompted, enter a name for your app. In this tutorial we are usingplugin-wrappers
as the app name:npx @backstage/create-app@0.5.2 --skip-install
@backstage/create-app
v0.5.2 is used because the Tanzu Developer Portal version that ships with Tanzu Application Platform v1.7 uses Backstage v1.15.0. Backstage v1.15.0 uses@backstage/create-app
v0.5.2. For more information, see the Backstage version manifest.Important Ensure that you use the correct versions of dependencies for your Tanzu Application Platform version. Use the Backstage version compatibility reference table to find which versions of Backstage dependencies work with your version of Tanzu Application Platform.
The
--skip-install
flag tells the script to not runyarn install
. This is because you will remove the unnecessary dependencies that would have been needed if you were building a traditional Backstage app before installing your dependencies.The
create-app
command scaffolds a Backstage project structure under a directory matching your project name. -
Run:
cd APP-NAME
Where
APP-NAME
is your application name. For example,plugin-wrappers
.
To remove unnecessary dependencies you need to:
-
Remove the packages directory by running:
rm -rf packages/
The packages directory contains a scaffolded Backstage
app
andbackend
, which are only necessary for a traditional Backstage app. -
Remove the packages directory from the
yarn
workspaces by deleting the"packages/*"
line within theworkspaces
attribute inpackage.json
. For example:diff --git a/package.json b/package.json index 00d64c9..77f38f3 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ }, "workspaces": { "packages": [ - "packages/*", "plugins/*" ] },
-
Install the dependencies by running:
yarn install --ignore-engines
This command installs
backstage-cli
and a few other dependencies. The--ignore-engines
flag is needed because a transitive dependency is expecting Node v18, but this Tanzu Developer Portal version currently only supports Node v16.
Now that you have an environment to develop your Tanzu Developer Portal plug-ins, you can begin wrapping Backstage plug-ins. You will start with the Tech Insight front-end plug-in.
This section describes how to generate a front-end plug-in.
-
Generate a front-end plug-in by running the following command replacing
PACKAGE-NAMESPACE
with your namespace (for example,@mycompany
):yarn backstage-cli new --select plugin --option id=tech-insights-wrapper --scope PACKAGE-NAMESPACE --no-private
Important The
yarn install
step of the previous command will fail because of a Node version issue. This is handled in a later step.Here is a summary of what the
backstage-cli new
script does:--select plugin
creates a front-end plug-in--option id=tech-insights-wrapper
names the plug-intech-insights-wrapper
--scope PACKAGE-NAMESPACE
scopes the package under thePACKAGE-NAMESPACE
namespace--no-private
sets the package to public
-
Open the
plugins/tech-insights-wrapper/package.json
to see how these options were mapped to the generatedpackage.json
.
Update your dependencies for the specific Backstage plug-in you want to wrap:
-
Replace the
dependencies
in thepackage.json
with:... "dependencies": { "@backstage/plugin-tech-insights": "0.3.11", "@backstage/plugin-catalog": "1.11.2", "@vmware-tanzu/core-common": "1.0.0", "@vmware-tanzu/core-frontend": "1.0.0" }, ...
-
The dependency on
@backstage/plugin-tech-insights
is obvious, but verify the version is compatible with your Tanzu Application Platform version by reading the Backstage version compatibility table and checking the version listed in the Backstage Manifest file for your specific Tanzu Application Portal version. -
@backstage/plugin-catalog
is needed for a UI component you will use later. Verify its version by using the Backstage version compatibility table. -
Verify that you are using the correct versions of
@vmware-tanzu/core-common
and@vmware-tanzu/core-frontend
by cross-referencing the dependency name with your Tanzu Application Platform version in the Tanzu Developer Portal plug-in libraries compatibility tables. You will use@vmware-tanzu/core-common
and@vmware-tanzu/core-frontend
later for integrating the Backstage plug-in with Tanzu Developer Portal. -
Install the dependencies you added by running:
cd plugins/tech-insights-wrapper yarn install --ignore-engines
The backstage-cli new
command created example code that you don't need. Remove this code and start
with an empty src
directory by running:
rm -rf dev/ src/ && mkdir src
-
Read the documentation for
@backstage/plugin-tech-insights
. You will see that in order to use this Backstage plug-in, you need to modify the contents of theserviceEntityPage
constant. Because you do not have access to the Tanzu Developer Portal source code, you cannot change that constant directly. Instead, you must use a surface to make the equivalent change. -
Create the file where you will use a surface to edit the
serviceEntityPage
constant by running:touch src/TechInsightsFrontendPlugin.tsx
-
In the
TechInsightsFrontendPlugin.tsx
file, add the following code:import { EntityLayout } from '@backstage/plugin-catalog'; import { EntityTechInsightsScorecardContent } from '@backstage/plugin-tech-insights'; import { AppPluginInterface, SurfaceStoreInterface, EntityPageSurface, } from '@vmware-tanzu/core-frontend'; import React from 'react'; export const TechInsightsFrontendPlugin: AppPluginInterface = () => (context: SurfaceStoreInterface) => { context.applyTo( EntityPageSurface, (entityPageSurface) => { entityPageSurface.servicePage.addTab( <EntityLayout.Route path="/techinsights" title="TechInsights"> <EntityTechInsightsScorecardContent title="TechInsights Scorecard." description="TechInsight's default fact-checkers" /> </EntityLayout.Route>, ); }, ); };
Where:
-
context.applyTo
is a function that takes the class of the surface you want to interact with, and a function that is passed the instance of that class. -
The
EntityPageSurface
keeps track of tabs that appear on the service page. You add a new tab by callingentityPageSurface.servicePage.addTab
and passing it the UI component you want it to render. -
TechInsightsFrontendPlugin: AppPluginInterface = () => (context: SurfaceStoreInterface) => {}
is boilerplate code that enables you to interact with the various front-end surfaces in Tanzu Developer Portal. -
EntityPageSurface
is one example of the many surfaces available in Tanzu Developer Portal. To discover all the surfaces currently available, see How to use surfaces. For surface API reference information, see API documentation for surfaces.
This code accomplishes the same thing as the @backstage/plugin-tech-insights, but for an integration with Tanzu Developer Portal instead of a traditional Backstage app.
-
To expose and then build the front-end plug-in:
-
Create an
index.ts
file under theplugins/tech-insights-wrapper/src
directory:touch src/index.ts
-
In the
index.ts
file write:export { TechInsightsFrontendPlugin as plugin } from './TechInsightsFrontendPlugin';
This exports
TechInsightsFrontendPlugin
in a way that enables Configurator to use your plug-in. You need to aliasTechInsightsFrontendPlugin
toplugin
because the Tanzu Developer Portal Configurator expects compatible plug-ins to export a symbol with the nameplugin
. -
Build your Tanzu Developer Portal plug-in by running:
yarn tsc && yarn build
You can now publish this plug-in to your npm registry. However, you cannot use the plug-in functions without the back-end portion.
Creating the back-end plug-in is very similar to the work you did for the front-end plug-in. This section does not describe in detail what is happening at each step except for where it differs from the previous work.
This describes how to generate a back-end plug-in.
From the root of your project, generate a back-end plug-in by running:
yarn backstage-cli new --select backend-plugin --option id=tech-insights-wrapper --scope PACKAGE-NAMESPACE --no-private
Where:
PACKAGE-NAMESPACE
is the namespace for your package. For example,@mycompany
.--select backend-plugin
tells thebackstage-cli
to generate a back-end plug-in.--option id=tech-insights-wrapper
provides the name for the plugin. Notice the ID you provide is the same as the front-end plug-in.backstage-cli
automatically appends-backend
to the directory and package-name of back-end plug-ins to prevent conflict with the front-end plug-in.
To add your dependencies for the specific Backstage plug-in you want to wrap:
-
Update the dependencies in
package.json
as follows:"dependencies": { "@backstage/plugin-tech-insights-backend": "0.5.12", "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.30", "@vmware-tanzu/core-backend": "1.0.0", "express": "4.18.2" },
-
Install your dependencies by running:
cd plugins/tech-insights-wrapper-backend/ yarn install --ignore-engines
-
Remove the Backstage scaffolded example code by running:
rm -rf src/ && mkdir src
-
Within the
src/
directory, create a file calledTechInsightsBackendPlugin.ts
by running:touch src/TechInsightsBackendPlugin.ts
-
In
TechInsightsBackendPlugin.ts
, add the following code:import { createRouter, buildTechInsightsContext, createFactRetrieverRegistration, entityOwnershipFactRetriever, entityMetadataFactRetriever, techdocsFactRetriever, } from '@backstage/plugin-tech-insights-backend'; import { Router } from 'express'; import { JsonRulesEngineFactCheckerFactory, JSON_RULE_ENGINE_CHECK_TYPE, } from '@backstage/plugin-tech-insights-backend-module-jsonfc'; import { BackendPluginInterface, BackendPluginSurface, PluginEnvironment, } from '@vmware-tanzu/core-backend'; const ttlTwoWeeks = { timeToLive: { weeks: 2 } }; export default async function createPlugin( env: PluginEnvironment, ): Promise<Router> { const techInsightsContext = await buildTechInsightsContext({ logger: env.logger, config: env.config, database: env.database, discovery: env.discovery, tokenManager: env.tokenManager, scheduler: env.scheduler, factRetrievers: [ createFactRetrieverRegistration({ cadence: '0 */6 * * *', // Run every 6 hours - https://crontab.guru/#0_*/6_*_*_* factRetriever: entityOwnershipFactRetriever, lifecycle: ttlTwoWeeks, }), createFactRetrieverRegistration({ cadence: '0 */6 * * *', factRetriever: entityMetadataFactRetriever, lifecycle: ttlTwoWeeks, }), createFactRetrieverRegistration({ cadence: '0 */6 * * *', factRetriever: techdocsFactRetriever, lifecycle: ttlTwoWeeks, }), ], factCheckerFactory: new JsonRulesEngineFactCheckerFactory({ logger: env.logger, checks: [ { id: 'groupOwnerCheck', type: JSON_RULE_ENGINE_CHECK_TYPE, name: 'Group Owner Check', description: 'Verifies that a Group has been set as the owner for this entity', factIds: ['entityOwnershipFactRetriever'], rule: { conditions: { all: [ { fact: 'hasGroupOwner', operator: 'equal', value: true, }, ], }, }, }, { id: 'titleCheck', type: JSON_RULE_ENGINE_CHECK_TYPE, name: 'Title Check', description: 'Verifies that a Title, used to improve readability, has been set for this entity', factIds: ['entityMetadataFactRetriever'], rule: { conditions: { all: [ { fact: 'hasTitle', operator: 'equal', value: true, }, ], }, }, }, { id: 'techDocsCheck', type: JSON_RULE_ENGINE_CHECK_TYPE, name: 'TechDocs Check', description: 'Verifies that TechDocs has been enabled for this entity', factIds: ['techdocsFactRetriever'], rule: { conditions: { all: [ { fact: 'hasAnnotationBackstageIoTechdocsRef', operator: 'equal', value: true, }, ], }, }, }, ], }), }); return await createRouter({ ...techInsightsContext, logger: env.logger, config: env.config, }); } export const TechInsightsBackendPlugin: BackendPluginInterface = () => surfaces => surfaces.applyTo(BackendPluginSurface, backendPluginSurface => { backendPluginSurface.addPlugin({ name: 'tech-insights', pluginFn: createPlugin, }); });
Note The majority of this code comes from the npm JS documentation. The Backstage plug-in documentation instructs you to create a constant for
techInsightsEnv
and then configure the router by usingapiRouter.use('/tech-insights', await techInsights(techInsightsEnv))
all in the Backstage source code. Because you are unable to edit the source code of Tanzu Developer Portal, the above code accomplished the same thing by: -
Getting an instance of the
BackendPluginSurface
. This surface keeps track of all the back-end plug-ins. -
Adding your plug-in by using the
addPlugin
function. Thename
argument is used to configure the path in the router.
To expose and then build the back-end plug-in:
-
Create an
index.ts
file by running:touch src/index.ts
-
In the
index.ts
file, write:export { TechInsightsBackendPlugin as plugin } from './TechInsightsBackendPlugin';
This exposes the Tanzu Developer Portal plug-in.
-
Build your plug-in by running:
yarn tsc && yarn build
You can now publish your Tanzu Developer Portal plug-ins to your registry of choice. If you want to publish your plug-ins to a private registry, see Configure the Configurator with a private registry.
After publishing your plug-ins, you can build a customized Tanzu Developer Portal with Configurator.