Artifacts Plugin for JFrog Artifactory brings your binary artifacts into Backstage. Built by StageCentral, it includes a front end plugin and a backend plugin. Together they connect Backstage catalog entities to the packages and folders you store in Artifactory. You get an Artifactory tab on each entity page with a list of related artifacts. Links open the right spot in your JFrog instance.
The plugin matches artifacts by name or by a property. The common property is catalog.component
. You can map an entity to a different identifier when needed. This keeps the view accurate even when repo names and service names differ.
Typical use cases are simple. Browse artifacts for a service without leaving Backstage. Check what was published after a build. Find the right version during a rollout. Review contents during an incident. Reduce context switching during code reviews and release planning.
The back end uses the official JFrog client for JavaScript. It focuses on Artifactory today. It can be extended to surface more data from the JFrog platform later.
If your org stores builds in Artifactory and tracks components in Backstage, this plugin gives you a clear bridge between the two. It puts artifacts where developers already work.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install the frontend package in your Backstage app.
yarn --cwd packages/app add @stagecentral/plugin-artifactory
Install the backend package in your Backstage backend.
yarn --cwd packages/backend add @stagecentral/plugin-artifactory-backend @backstage/catalog-client
Add Artifactory config. Put this in your app config. Use environment variables for secrets.
# app-config.yaml
artifactory:
url: https://myorg.jfrog.io
user: ${ARTIFACTORY_USER}
password: ${ARTIFACTORY_PASSWORD}
# optional. change the matching property if you do not use the default
# artifactProperty: catalog.component
The url should be the Jfrog platform host. The plugin adds the artifactory path on its own.
To wire the backend on the classic (legacy) backend system. Create the plugin router file:
// packages/backend/src/plugins/artifactory.ts
import { createRouter } from '@stagecentral/plugin-artifactory-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
return await createRouter({
logger: env.logger,
config: env.config,
catalogApi,
});
}
Mount it in the backend index file.
// packages/backend/src/index.ts
import artifactory from './plugins/artifactory';
// create the env near your other envs
const artifactoryEnv = useHotMemoize(module, () => createEnv('artifactory'));
// mount the router near your other routes
apiRouter.use('/artifactory', await artifactory(artifactoryEnv));
To wire the backend on the new backend system. Keep the same artifactory.ts file shown above. Then bridge it with the legacy helper.
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import { legacyPlugin } from '@backstage/backend-common';
const backend = createBackend();
// this mounts the old style router at /api/artifactory
backend.add(legacyPlugin('artifactory', import('./plugins/artifactory')));
backend.start();
Add the Artifactory tab in the app so users can see it on entity pages. Open your entity page component and add the route.
// packages/app/src/components/catalog/EntityPage.tsx
import { EntityArtifactoryContent } from '@stagecentral/plugin-artifactory';
import { EntityLayout } from '@backstage/plugin-catalog';
const serviceEntityPage = (
<EntityLayout>
{/* other routes */}
<EntityLayout.Route path="/artifactory" title="Artifactory">
<EntityArtifactoryContent />
</EntityLayout.Route>
</EntityLayout>
);
Connect your catalog entities to artifacts. By default the plugin matches artifacts by either a property or a path that equals the entity name. You can change the property key with artifactory.artifactProperty in app config. You can also override the match id per entity with an annotation.
# catalog-info.yaml for a component
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: my-service
annotations:
backstage.io/artifactory-id: my-service-alt-id
spec:
type: service
owner: team-a
lifecycle: production
Things to Know
The plugin adds an Artifactory tab on each entity page. It shows a list of folders that match the entity. Click a row to open that folder in your JFrog instance. The link uses the JFrog host from your app config. The plugin adds the artifactory path on its own.
Artifacts are matched by either a property or by a path that equals the entity name. The default property key is catalog.component. You can set a different key in app config.
# app-config.yaml
artifactory:
url: https://myorg.jfrog.io
user: ${ARTIFACTORY_USER}
password: ${ARTIFACTORY_PASSWORD}
artifactProperty: my.custom.property
You can override the match id per entity with an annotation. Use this when the artifact folder or property value does not match the entity name.
# catalog-info.yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payments-api
annotations:
backstage.io/artifactory-id: payments-service
spec:
type: service
owner: team-a
lifecycle: production
The backend uses the JFrog client for JavaScript and runs queries using AQL. This is the default query. You do not need to set it. This is for reference.
items.find({"$or":[{"@ARTIFACT_PROPERTY":{"$eq":"ENTITY_NAME"},"path":{"$eq":"ENTITY_NAME"}}],"type":"folder","path":{"$ne":"."},"name":{"$ne":"_uploads"}})
Authentication uses a JFrog user and password. Put those in environment variables that your Backstage process can read. Tokens are not supported yet by the plugin. Create a service account in JFrog with read scope for the repos you want to expose. Do not hardcode secrets in source files.
Use the JFrog platform host in your config. For JFrog Cloud use a host like https://myorg.jfrog.io. For self hosted use your own host. The plugin appends the artifactory path when building links and when calling the API.
The frontend expects the backend to be available under api artifactory. Keep the backend mount at that path as shown in the install steps. The frontend will call that route to fetch data.
Common tweaks
- Change the property key across the app. Set artifactory.artifactProperty in app config. Then add that property to artifact folders in Artifactory with values matching your Backstage ids.
- Change the match id for a single service. Add the backstage.io artifactory-id annotation to the entity. The value should match the artifact folder name or the property value stored on the folder in Artifactory.
Example of a minimal config for production use
# app-config.yaml
artifactory:
url: https://myorg.jfrog.io
user: ${ARTIFACTORY_USER}
password: ${ARTIFACTORY_PASSWORD}
# optional
# artifactProperty: catalog.component
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.