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:
nvm
is recommended. For how to install nvm
, see the nvm
GitHub repository. For how to install a specific version of nvm
, see the NodeJS documentation.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.
This topic tells you, by way of example, how to wrap the Backstage TechInsights plug-in. 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.
This tutorial uses plugin-wrappers
as the app name. Run the create-app
script and, when prompted, enter a name for your app:
npx @backstage/[email protected] --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.
ImportantEnsure 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 run yarn 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 must:
Remove the packages directory by running:
rm -rf packages/
The packages directory contains a scaffolded Backstage app
and backend
, which are only necessary for a traditional Backstage app.
Remove the packages directory from the yarn
workspaces by deleting the "packages/*"
line within the workspaces
attribute in package.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
ImportantThe
yarn install
step of the previous command fails 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-in tech-insights-wrapper
--scope PACKAGE-NAMESPACE
scopes the package under the PACKAGE-NAMESPACE
namespace--no-private
sets the package to publicOpen the plugins/tech-insights-wrapper/package.json
to see how these options were mapped to the generated package.json
.
Update your dependencies for the specific Backstage plug-in you want to wrap:
Replace the dependencies
in the package.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 that you 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 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 to use this Backstage plug-in, you must edit the content of the serviceEntityPage 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 calling entityPageSurface.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 the plugins/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 alias TechInsightsFrontendPlugin
to plugin
because the Tanzu Developer Portal Configurator expects compatible plug-ins to export a symbol with the name plugin
.
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 the backstage-cli
to generate a back-end plug-in.--option id=tech-insights-wrapper
provides the name for the plug-in. 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 called TechInsightsBackendPlugin.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,
});
});
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 using apiRouter.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, this code accomplishes the same thing by:
BackendPluginSurface
. This surface keeps track of all the back-end plug-ins.addPlugin
function. The name
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.