Merge branch 'master' into github-installations-limit

This commit is contained in:
Brian Fletcher
2021-07-14 08:03:08 +01:00
committed by GitHub
3877 changed files with 39360 additions and 53613 deletions
+22 -18
View File
@@ -23,18 +23,18 @@ during their entire life cycle.
Each Utility API is tied to an `ApiRef` instance, which is a global singleton
object without any additional state or functionality, its only purpose is to
reference Utility APIs. `ApiRef`s are created using `createApiRef`, which is
exported by `@backstage/core`. There are many
exported by `@backstage/core-plugin-api`. There are many
[predefined Utility APIs](../reference/utility-apis/README.md) defined in
`@backstage/core`, and they're all exported with a name of the pattern
`*ApiRef`, for example `errorApiRef`.
`@backstage/core-plugin-api`, and they're all exported with a name of the
pattern `*ApiRef`, for example `errorApiRef`.
To access one of the Utility APIs inside a React component, use the `useApi`
hook exported by `@backstage/core`, or the `withApis` HOC if you prefer class
components. For example, the `ErrorApi` can be accessed like this:
hook exported by `@backstage/core-plugin-api`, or the `withApis` HOC if you
prefer class components. For example, the `ErrorApi` can be accessed like this:
```tsx
import React from 'react';
import { useApi, errorApiRef } from '@backstage/core';
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
export const MyComponent = () => {
const errorApi = useApi(errorApiRef);
@@ -52,9 +52,9 @@ Note that there is no explicit type given for `ErrorApi`. This is because the
`errorApiRef` has the type embedded, and `useApi` is able to infer the type.
Also note that consuming Utility APIs is not limited to plugins, it can be done
from any component inside Backstage, including the ones in `@backstage/core`.
The only requirement is that they are beneath the `AppProvider` in the react
tree.
from any component inside Backstage, including the ones in
`@backstage/core-plugin-api`. The only requirement is that they are beneath the
`AppProvider` in the react tree.
## Supplying APIs
@@ -71,8 +71,11 @@ For example, this is the default `ApiFactory` for the `ErrorApi`:
createApiFactory({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) =>
new ErrorAlerter(alertApi, new ErrorApiForwarder()),
factory: ({ alertApi }) => {
const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder());
UnhandledErrorForwarder.forward(errorApi, { hidden: false });
return errorApi;
},
});
```
@@ -98,13 +101,13 @@ app, and the app itself.
### Core APIs
Starting with the Backstage core library, it provides implementations for all of
the core APIs. The core APIs are the ones exported by `@backstage/core`, such as
the `errorApiRef` and `configApiRef`. You can find a full list of them
[here](../reference/utility-apis/README.md).
the core APIs. The core APIs are the ones exported by
`@backstage/core-plugin-api`, such as the `errorApiRef` and `configApiRef`. You
can find a full list of them [here](../reference/utility-apis/README.md).
The core APIs are loaded for any app created with `createApp` from
`@backstage/core`, which means that there is no step that needs to be taken to
include these APIs in an app.
`@backstage/core-plugin-api`, which means that there is no step that needs to be
taken to include these APIs in an app.
### Plugin APIs
@@ -210,8 +213,9 @@ implement the `ErrorApi`, as it is checked by the type embedded in the
Plugins are free to define their own Utility APIs. Simply define the TypeScript
interface for the API, and create an `ApiRef` using `createApiRef` exported from
`@backstage/core`. Also be sure to provide at least one implementation of the
API, and to declare a default factory for the API in `createPlugin`.
`@backstage/core-plugin-api`. Also be sure to provide at least one
implementation of the API, and to declare a default factory for the API in
`createPlugin`.
Custom Utility APIs can be either public or private, which is up to the plugin
to choose. Private APIs do not expose an external API surface, and it's
@@ -6,8 +6,8 @@ description: Architecture Decision Record (ADR) log on Module Export Structure
## Context
With a growing number of exports of packages like `@backstage/core`, it is
becoming more and more difficult to answer questions such as
With a growing number of exports of packages like `@backstage/core-components`,
it is becoming more and more difficult to answer questions such as
> Is the export in this module also exported by the package?
@@ -86,7 +86,7 @@ import { helperFunc } from '../../lib/UtilityX/helper';
## Consequences
We will actively work to rework the export structure in our codebase,
prioritizing the library packages such as `@backstage/core` and
prioritizing the library packages such as `@backstage/core-components` and
`@backstage/backend-common`.
If possible, we will add tools, such as lint rules, to help enforce the export
@@ -35,6 +35,8 @@ example `catalog` or `techdocs`):
- `x`: Contains the main frontend code of the plugin.
- `x-backend`: Contains the main backend code of the plugin.
- `x-backend-module-<name>`: Contains optional modules related to the backend
plugin.
- `x-react`: Contains shared widgets, hooks and similar that both the plugin
itself (`x`) and third-party frontend plugins can depend on.
- `x-node`: Contains utilities for backends that both the plugin backend itself
@@ -61,6 +63,10 @@ We will actively migrate existing packages that are part of a plugin to the
`plugins/catalog-common` we might want to do an exception here, as it's a very
central package.
We will actively migrate optional features of backend plugins into separate
`x-backend-module-<name>` packages, for example the more specialized processors
in the catalog backend.
The limited set of rules might not be sufficient in the future. If additional
packages are required, we will revisit this decision and extend the pattern.

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 303 KiB

+150
View File
@@ -0,0 +1,150 @@
---
id: identity-resolver
title: Identity resolver
description: Identity resolvers of Backstage users after they sign-in
---
This guide explains how the identity of a Backstage user is stored inside their
Backstage Identity Token and how you can customize the Sign-In resolvers to
include identity and group membership information of the user from other
external systems. This ultimately helps with determining the ownership of a
Backstage entity by a user. The ideas here were originally proposed in the RFC
[#4089](https://github.com/backstage/backstage/issues/4089).
When a user signs in to Backstage, inside the `claims` field of their Backstage
Token (which are standard JWT tokens) a special `ent` claim is set. `ent`
contains a list of
[entity references](../features/software-catalog/references.md), each of which
denotes an identity or a membership that is relevant to the user. There is no
guarantee that these correspond to actual existing catalog entities.
Let's take an example sign-in resolver for the Google auth provider and explore
how the `ent` field inside `claims` can be set.
Inside your `packages/backend/src/plugins/auth.ts` file, you can provide custom
sign-in resolvers and set them for any of the Authentication providers inside
`providerFactories` of the `createRouter` imported from the
`@backstage/plugin-auth-backend` plugin.
```ts
export default async function createPlugin({
...
}: PluginEnvironment): Promise<Router> {
return await createRouter({
...
providerFactories: {
google: createGoogleProvider({
signIn: {
resolver: async ({ profile: { email } }, ctx) => {
// Call a custom validator function that checks that the email is
// valid and on our own company's domain, and throws an Error if it
// isn't
validateEmail(email);
// List of entity references that denote the identity and
// membership of the user
const ent = [];
// Let's use the username in the email ID as the user's default
// unique identifier inside Backstage.
const [id] = email.split('@');
ent.push(`User:default/${id}`)
// Let's call the internal LDAP provider to get a list of groups
// that the user belongs to, and add those to the list as well
const ldapGroups = await getLdapGroups(email);
ldapGroups.forEach(group => ent.push(`Group:default/${group}`))
// Issue the token containing the entity claims
const token = await ctx.tokenIssuer.issueToken({
claims: { sub: id, ent },
});
return { id, token };
},
},
}),
},
});
}
```
As you can see, the generated Backstage Token now contains all the claims about
the identity and membership of the user. Once the sign-in process is complete,
and we need to find out if a user owns an Entity in the Software Catalog, these
`ent` claims can be used to determine the ownership.
According to the RFC, the definition of the ownership of an entity E, for a user
U, is as follows:
- Get all the `ownedBy` relations of E, and call them O
- Get all the claims of the user U and call them C
- If any C matches any O, return `true`
- Get all Group entities that U is a member of, using the regular
`memberOf`/`hasMember` relation mechanism, and call them G
- If any G matches any O, return `true`
- Otherwise, return `false`
## Default sign-in resolvers
Of course you don't have to customize the sign-in resolver if you don't need to.
The Auth backend plugin comes with a set of default sign-in resolvers which you
can use. For example - the Google provider has a default email-based sign-in
resolver, which will search the catalog for a single user entity that has a
matching `google.com/email` annotation.
It can be enabled like this
```tsx
// File: packages/backend/src/plugins/auth.ts
import { googleEmailSignInResolver } from '@backstage/plugin-auth-backend';
export default async function createPlugin({
...
}: PluginEnvironment): Promise<Router> {
return await createRouter({
...
providerFactories: {
google: createGoogleProvider({
signIn: {
resolver: googleEmailSignInResolver
}
...
```
## AuthHandler
Similar to a custom sign-in resolver, you can also write a custom auth handler
function which is used to verify and convert the auth response into the profile
that will be presented to the user. This is where you can customize things like
display name and profile picture.
This is also the place where you can do authorization and validation of the user
and throw errors if the user should not be allowed access in Backstage.
```tsx
// File: packages/backend/src/plugins/auth.ts
export default async function createPlugin({
...
}: PluginEnvironment): Promise<Router> {
return await createRouter({
...
providerFactories: {
google: createGoogleProvider({
authHandler: async ({
fullProfile // Type: passport.Profile,
idToken // Type: (Optional) string,
}) => {
// Custom validation code goes here
return {
profile: {
email,
picture,
displayName,
}
};
}
})
}
})
}
```
+2 -1
View File
@@ -66,7 +66,8 @@ built-in providers:
```diff
# packages/app/src/App.tsx
+ import { githubAuthApiRef, SignInProviderConfig, SignInPage } from '@backstage/core';
+ import { githubAuthApiRef } from '@backstage/core-plugin-api';
+ import { SignInProviderConfig, SignInPage } from '@backstage/core-components';
+ const githubProvider: SignInProviderConfig = {
+ id: 'github-auth-provider',
+4 -4
View File
@@ -14,7 +14,7 @@ to various third party APIs.
There are occasions when the user wants to perform actions towards third party
services that require authorization via OAuth. Backstage provides standardized
[Utility APIs](../api/utility-apis.md) such as the
[GoogleAuthApi](https://github.com/backstage/backstage/blob/master/packages/core-api/src/apis/definitions/auth.ts)
[GoogleAuthApi](https://github.com/backstage/backstage/blob/master/packages/core-plugin-api/src/apis/definitions/auth.ts)
for that use-case. Backstage also includes a set of implementations of these
APIs that integrate with the
[auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend)
@@ -38,7 +38,7 @@ choose an account to log in with, and accept or reject the request. If the user
accepts the login request, a token is issued, and any holder of the token can
use it to make authenticated requests towards the third party service.
## OAuth in @backstage/core-api and auth-backend
## OAuth in @backstage/core-app-api and auth-backend
The default OAuth implementation in Backstage is based on an OAuth server-side
offline access flow, which means that it uses the backend as a helper in order
@@ -59,8 +59,8 @@ easier to make authenticated requests inside a plugin.
The following describes the OAuth flow implemented by the
[auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend)
and
[DefaultAuthConnector](https://github.com/backstage/backstage/blob/master/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts)
in `@backstage/core-api`.
[DefaultAuthConnector](https://github.com/backstage/backstage/blob/master/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.ts)
in `@backstage/core-app-api`.
Component and APIs can request Access or ID Tokens from any available Auth
provider. If there already exists a cached fresh token that covers (at least)
+9 -8
View File
@@ -45,8 +45,8 @@ pieces in place that can be used.
#### Identity for Plugin Developers
As a plugin developer, there are two main touchpoints for identities: the
`IdentityApi` exported by `@backstage/core` via the `identityApiRef`, and a not
yet existing middleware exported by `@backstage/backend-common`.
`IdentityApi` exported by `@backstage/core-plugin-api` via the `identityApiRef`,
and a not yet existing middleware exported by `@backstage/backend-common`.
The `IdentityApi` gives access to the signed-in user's identity in the frontend.
It provides access to the user's ID, lightweight profile information, and an ID
@@ -61,8 +61,9 @@ https://github.com/backstage/backstage/issues/1435.
If you're setting up your own Backstage app, or want to add a new identity
provider, there are three touchpoints: the frontend auth APIs in
`@backstage/core-api`, the backend auth providers in `auth-backend`, and the
`SignInPage` component configured in the Backstage app via `createApp`.
`@backstage/core-app-api` and `@backstage/core-plugin-api`, the backend auth
providers in `auth-backend`, and the `SignInPage` component configured in the
Backstage app via `createApp`.
The frontend APIs and backend providers are tightly coupled together for each
auth provider, and together they implement an e2e auth flow. Only some auth
@@ -81,10 +82,10 @@ The final piece of the puzzle is the `SignInPage` component that can be
configured as part of the app. Without a sign-in page, Backstage will fall back
to a `guest` identity for all users, without any ID token. To enable sign-in, a
`SignInPage` needs to be configured, which in turn has to supply a user to the
app. The `@backstage/core` package provides a basic sign-in page that allows
both the user and the app developer to choose between a couple of different
sign-in methods, or to designate a single provider that may also be logged in to
automatically.
app. The `@backstage/core-components` package provides a basic sign-in page that
allows both the user and the app developer to choose between a couple of
different sign-in methods, or to designate a single provider that may also be
logged in to automatically.
## Further Reading
+1 -3
View File
@@ -554,9 +554,7 @@ Options:
Scope: `root`
Validate `@backstage` dependencies within the repo, making sure that there are
no duplicates of packages that might lead to breakages. For example,
`@backstage/core` must not be loaded in twice, so having two different versions
of it installed will cause this command to exit with an error.
no duplicates of packages that might lead to breakages.
By supplying the `--fix` flag the command will attempt to fix any conflict that
can be resolved by editing `yarn.lock`, but will not attempt to search for
+15
View File
@@ -93,6 +93,21 @@ declare the visibility of a leaf node of `type: "string"`.
| `backend` | (Default) Only in backend |
| `secret` | Only in backend and may be excluded from logs for security reasons |
You can set visibility with an `@visibility` comment in the `Config` Typescript
interface.
```ts
export interface Config {
app: {
/**
* Frontend root URL
* @visibility frontend
*/
baseUrl: string;
};
}
```
## Validation
Schemas can be validated using the `backstage-cli config:check` command. If you
+2 -2
View File
@@ -112,7 +112,7 @@ example `getString`. These will throw an error if there is no value available.
The [ConfigApi](../reference/utility-apis/Config.md) in the frontend is a
[UtilityApi](../api/utility-apis.md). It's accessible as usual via the
`configApiRef` exported from `@backstage/core`.
`configApiRef` exported from `@backstage/core-plugin-api`.
Depending on the config api in another API is slightly different though, as the
`ConfigApi` implementation is supplied via the App itself and not instantiated
@@ -123,7 +123,7 @@ for an example of how this wiring is done.
For standalone plugin setups in `dev/index.ts`, register a factory with a
statically mocked implementation of the config API. Use the `ConfigReader` from
`@backstage/config` to create an instance and register it for the `configApiRef`
from `@backstage/core`.
from `@backstage/core-plugin-api`.
## Accessing ConfigApi in Backend Plugins
+4 -3
View File
@@ -59,16 +59,17 @@ Once the host build is complete, we are ready to build our image. The following
FROM node:14-buster-slim
WORKDIR /app
# Copy repo skeleton first, to avoid unnecessary docker cache invalidation.
# The skeleton contains the package.json of each package in the monorepo,
# and along with yarn.lock and the root package.json, that's enough to run yarn install.
ADD yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./
COPY yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./
RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz
RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)"
# Then copy the rest of the backend bundle, along with any other files we might want.
ADD packages/backend/dist/bundle.tar.gz app-config.yaml ./
COPY packages/backend/dist/bundle.tar.gz app-config.yaml ./
RUN tar xzf bundle.tar.gz && rm bundle.tar.gz
CMD ["node", "packages/backend", "--config", "app-config.yaml"]
```
+1 -1
View File
@@ -25,7 +25,7 @@ component, which are then displayed both visually and with sample code to be
copied.
When custom Backstage components are created, they are placed in the
`@backstage/core` package and added to the Storybook.
`@backstage/core-components` package and added to the Storybook.
There may be times where an existing Material-UI component (in
`@material-ui/core`) is sufficient and doesn't need to be wrapped or duplicated.
+2 -2
View File
@@ -68,7 +68,7 @@ The base URL to the Kubernetes control plane. Can be found by using the
##### `clusters.\*.name`
A name to represent this cluster, this must be unique within the `clusters`
array. Users will see this value in the Service Catalog Kubernetes plugin.
array. Users will see this value in the Software Catalog Kubernetes plugin.
##### `clusters.\*.authProvider`
@@ -195,7 +195,7 @@ annotations:
#### Labeling Kubernetes components
In order for Kubernetes components to show up in the service catalog as a part
In order for Kubernetes components to show up in the software catalog as a part
of an entity, Kubernetes components themselves can have the following label:
```yaml
+1 -1
View File
@@ -2,7 +2,7 @@
id: overview
title: Kubernetes
sidebar_label: Overview
description: Monitoring Kubernetes based services with the service catalog
description: Monitoring Kubernetes based services with the software catalog
---
Kubernetes in Backstage is a tool that's designed around the needs of service
+6 -8
View File
@@ -29,7 +29,7 @@ Backstage app with the following contents:
```tsx
import React from 'react';
import { Content, Header, Page } from '@backstage/core';
import { Content, Header, Page } from '@backstage/core-components';
import { Grid, List, Card, CardContent } from '@material-ui/core';
import {
SearchBar,
@@ -142,7 +142,6 @@ export default async function createPlugin({
const indexBuilder = new IndexBuilder({ logger, searchEngine });
indexBuilder.addCollator({
type: 'software-catalog',
defaultRefreshIntervalSeconds: 600,
collator: new DefaultCatalogCollator({ discovery }),
});
@@ -262,21 +261,21 @@ const indexBuilder = new IndexBuilder({ logger, searchEngine });
```
Backstage Search can be used to power search of anything! Plugins like the
Catalog offer default [collators](./concepts.md#collators) which are responsible
for providing documents [to be indexed](./concepts.md#documents-and-indices).
You can register any number of collators with the `IndexBuilder` like this:
Catalog offer default [collators](./concepts.md#collators) (e.g.
[DefaultCatalogCollator](https://github.com/backstage/backstage/blob/df12cc25aa4934a98bc42ed03c07f64a1a0a9d72/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts))
which are responsible for providing documents
[to be indexed](./concepts.md#documents-and-indices). You can register any
number of collators with the `IndexBuilder` like this:
```typescript
const indexBuilder = new IndexBuilder({ logger, searchEngine });
indexBuilder.addCollator({
type: 'software-catalog',
defaultRefreshIntervalSeconds: 600,
collator: new DefaultCatalogCollator({ discovery }),
});
indexBuilder.addCollator({
type: 'my-custom-stuff',
defaultRefreshIntervalSeconds: 3600,
collator: new MyCustomCollator(),
});
@@ -290,7 +289,6 @@ its `defaultRefreshIntervalSeconds` value, like this:
```typescript {3}
indexBuilder.addCollator({
type: 'software-catalog',
defaultRefreshIntervalSeconds: 600,
collator: new DefaultCatalogCollator({ discovery }),
});
@@ -654,7 +654,7 @@ spec:
steps:
- id: fetch-base
name: Fetch Base
action: fetch:cookiecutter
action: fetch:template
input:
url: ./template
values:
+14 -15
View File
@@ -1,29 +1,29 @@
---
id: software-catalog-overview
title: Backstage Service Catalog (alpha)
title: Backstage Software Catalog (alpha)
sidebar_label: Overview
# prettier-ignore
description: The Backstage Service Catalog — actually, a software catalog, since it includes more than just services
description: The Backstage Software Catalog
---
## What is a Service Catalog?
## What is a Software Catalog?
The Backstage Service Catalog — actually, a software catalog, since it includes
The Backstage Software Catalog — actually, a software catalog, since it includes
more than just services — is a centralized system that keeps track of ownership
and metadata for all the software in your ecosystem (services, websites,
libraries, data pipelines, etc). The catalog is built around the concept of
[metadata YAML files](descriptor-format.md) stored together with the code, which
are then harvested and visualized in Backstage.
![service-catalog](https://backstage.io/blog/assets/6/header.png)
![software-catalog](https://backstage.io/blog/assets/6/header.png)
## How it works
Backstage and the Backstage Service Catalog make it easy for one team to manage
Backstage and the Backstage Software Catalog make it easy for one team to manage
10 services — and makes it possible for your company to manage thousands of
them.
More specifically, the Service Catalog enables two main use-cases:
More specifically, the Software Catalog enables two main use-cases:
1. Helping teams manage and maintain the software they own. Teams get a uniform
view of all their software; services, libraries, websites, ML models — you
@@ -34,15 +34,14 @@ More specifically, the Service Catalog enables two main use-cases:
## Getting Started
The Software Catalog is available to browse at `/catalog`. If you've followed
[Installing in your Backstage App](./installation.md) in your separate App or
[Getting Started with Backstage](../../getting-started) for this repo, you
should be able to browse the catalog at `http://localhost:3000`.
[Getting Started with Backstage](../../getting-started), you should be able to
browse the catalog at `http://localhost:3000`.
![](../../assets/software-catalog/service-catalog-home.png)
![](../../assets/software-catalog/software-catalog-home.png)
## Adding components to the catalog
The source of truth for the components in your service catalog are
The source of truth for the components in your software catalog are
[metadata YAML files](descriptor-format.md) stored in source control (GitHub,
GitHub Enterprise, GitLab, ...).
@@ -105,11 +104,11 @@ them, and do so using their normal Git workflow.
![](../../assets/software-catalog/bsc-edit.png)
Once the change has been merged, Backstage will automatically show the updated
metadata in the service catalog after a short while.
metadata in the software catalog after a short while.
## Finding software in the catalog
By default the service catalog shows components owned by the team of the logged
By default the software catalog shows components owned by the team of the logged
in user. But you can also switch to _All_ to see all the components across your
company's software ecosystem. Basic inline _search_ and _column filtering_ makes
it easy to browse a big set of components.
@@ -125,7 +124,7 @@ _starring_ of components:
## Integrated tooling through plugins
The service catalog is a great way to organize the infrastructure tools you use
The software catalog is a great way to organize the infrastructure tools you use
to manage the software. This is how Backstage creates one developer portal for
all your tools. Rather than asking teams to jump between different
infrastructure UIs (and incurring additional cognitive overhead each time they
@@ -1,177 +0,0 @@
---
id: installation
title: Installing in your Backstage App
description: Documentation on How to install Backstage Plugin
---
The catalog plugin comes in two packages, `@backstage/plugin-catalog` and
`@backstage/plugin-catalog-backend`. Each has their own installation steps,
outlined below.
## Installing @backstage/plugin-catalog
> **Note that if you used `npx @backstage/create-app`, the plugin is already
> installed and you can skip to
> [adding entries to the catalog](#adding-entries-to-the-catalog)**
The catalog frontend plugin should be installed in your `app` package, which is
created as a part of `@backstage/create-app`. To install the package, run:
```bash
# From your Backstage root directory
cd packages/app
yarn add @backstage/plugin-catalog
```
### Adding the Plugin to your `packages/app`
Add the two pages that the catalog plugin provides to your app. You can choose
any name for these routes, but we recommend the following:
```tsx
// packages/app/src/App.tsx
import {
catalogPlugin,
CatalogIndexPage,
CatalogEntityPage,
} from '@backstage/plugin-catalog';
// Add to the top-level routes, directly within <FlatRoutes>
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route path="/catalog/:namespace/:kind/:name" element={<CatalogEntityPage />}>
{/*
This is the root of the custom entity pages for your app, refer to the example app
in the main repo or the output of @backstage/create-app for an example
*/}
<EntityPage />
</Route>
```
The catalog plugin also has one external route that needs to be bound for it to
function: the `createComponent` route which should link to the page where the
user can create components. In a typical setup the create component route will
be linked to the Scaffolder plugin's template index page:
```ts
// packages/app/src/App.tsx
import { catalogPlugin } from '@backstage/plugin-catalog';
import { scaffolderPlugin } from '@backstage/plugin-scaffolder';
const app = createApp({
// ...
bindRoutes({ bind }) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
});
},
});
```
You may also want to add a link to the catalog index page to your sidebar:
```tsx
// packages/app/src/components/Root.tsx
import HomeIcon from '@material-ui/icons/Home';
// Somewhere within the <Sidebar>
<SidebarItem icon={HomeIcon} to="/catalog" text="Home" />;
```
This is all that is needed for the frontend part of the Catalog plugin to work!
## Gotchas that we will fix
Since the catalog plugin currently ships with a sentry plugin `InfoCard`
installed by default, you'll need to set `sentry.organization` in your
`app-config.yaml`. For example:
```yaml
sentry:
organization: Acme Corporation
```
If you've created an app with an older version of `@backstage/create-app` or
`@backstage/cli create-app`, be sure to remove the Welcome plugin from the app,
as that will conflict with the catalog routes.
## Installing @backstage/plugin-catalog-backend
> **Note that if you used `npx @backstage/create-app`, the plugin is already
> installed and you can skip to
> [adding entries to the catalog](#adding-entries-to-the-catalog)**
The catalog backend should be installed in your `backend` package, which is
created as a part of `@backstage/create-app`. To install the package, run:
```bash
# From your Backstage root directory
cd packages/backend
yarn add @backstage/plugin-catalog-backend
```
### Adding the Plugin to your `packages/backend`
You'll need to add the plugin to the `backend`'s router. You can do this by
creating a file called `packages/backend/src/plugins/catalog.ts` with contents
matching
[catalog.ts in the create-app template](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/packages/backend/src/plugins/catalog.ts).
Once the `catalog.ts` router setup file is in place, add the router to
`packages/backend/src/index.ts`:
```ts
import catalog from './plugins/catalog';
const catalogEnv = useHotMemoize(module, () => createEnv('catalog'));
const apiRouter = Router();
/** several different routers */
apiRouter.use('/catalog', await catalog(catalogEnv));
```
### Adding Entries to the Catalog
At this point the catalog backend is installed in your backend package, but you
will not have any entities loaded.
To get up and running and try out some templates quickly, you can add some of
our example templates through static configuration. Add the following to the
`catalog.locations` section in your `app-config.yaml`:
```yaml
catalog:
locations:
# Backstage Example Components
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/playback-order-component.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/podcast-api-component.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/queue-proxy-component.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/searcher-component.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/playback-lib-component.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/www-artist-component.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/shuffle-api-component.yaml
```
### Running the Backend
Finally, start up Backstage with the new configuration:
```bash
# Run from the root to start both backend and frontend
yarn dev
# Alternatively, run only the backend from its own package
cd packages/backend
yarn start
```
If you've also set up the frontend plugin, you should be ready to go browse the
catalog at [localhost:3000](http://localhost:3000) now!
@@ -51,7 +51,7 @@ spec:
steps:
- id: fetch-base
name: Fetch Base
action: fetch:cookiecutter
action: fetch:template
input:
url: ./template
values:
@@ -83,7 +83,7 @@ spec:
[Template Entity](../software-catalog/descriptor-format.md#kind-template)
contains more information about the required fields.
Once we have a `template.yaml` ready, we can then add it to the service catalog
Once we have a `template.yaml` ready, we can then add it to the software catalog
for use by the scaffolder.
You can add the template files to the catalog through
@@ -14,3 +14,54 @@ Azure, GitLab and Bitbucket.
A list of all registered actions can be found under `/create/actions`. For local
development you should be able to reach them at
`http://localhost:3000/create/actions`.
### Migrating from `fetch:cookiecutter` to `fetch:template`
The `fetch:template` action is a new action with a similar API to
`fetch:cookiecutter` but no dependency on `cookiecutter`. There are two options
for migrating templates that use `fetch:cookiecutter` to use `fetch:template`:
#### Using `cookiecutterCompat` mode
The new `fetch:template` action has a `cookiecutterCompat` flag which should
allow most templates built for `fetch:cookiecutter` to work without any changes.
1. Update action name in `template.yaml`. The name should be changed from
`fetch:cookiecutter` to `fetch:template`.
2. Set `cookiecutterCompat` to `true` in the `fetch:template` step input in
`template.yaml`.
```diff
steps:
- id: fetch-base
name: Fetch Base
- action: fetch:cookiecutter
+ action: fetch:template
input:
url: ./skeleton
+ cookiecutterCompat: true
values:
```
#### Manual migration
If you prefer, you can manually migrate your templates to avoid the need for
enabling cookiecutter compatibility mode, which will result in slightly less
verbose template variables expressions.
1. Update action name in `template.yaml`. The name should be changed from
`fetch:cookiecutter` to `fetch:template`.
2. Update variable syntax in file names and content. `fetch:cookiecutter`
expects variables to be enclosed in `{{` `}}` and prefixed with
`cookiecutter.`, while `fetch:template` expects variables to be enclosed in
`${{` `}}` and prefixed with `values.`. For example, a reference to variable
`myInputVariable` would need to be migrated from
`{{ cookiecutter.myInputVariable }}` to `${{ values.myInputVariable }}`.
3. Replace uses of `jsonify` with `dump`. The
[`jsonify` filter](https://cookiecutter.readthedocs.io/en/latest/advanced/template_extensions.html#jsonify-extension)
is built in to `cookiecutter`, and is not available by default when using
`fetch:template`. The
[`dump` filter](https://mozilla.github.io/nunjucks/templating.html#dump) is
the equivalent filter in nunjucks, so an expression like
`{{ cookiecutter.myAwesomeList | jsonify }}` should be migrated to
`${{ values.myAwesomeList | dump }}`.
@@ -0,0 +1,52 @@
---
id: configuration
title: Software Template Configuration
sidebar_label: Configuration
description: Configuration options for Backstage Software Templates
---
Backstage software templates create source code, so your Backstage application
needs to be set up to allow repository creation.
This is done in your `app-config.yaml` by adding
[Backstage integrations](https://backstage.io/docs/integrations/) for the
appropriate source code repository for your organization.
> Note: Integrations may already be set up as part of your `app-config.yaml`.
The next step is to add
[add templates](http://backstage.io/docs/features/software-templates/adding-templates)
to your Backstage app.
### GitHub
For GitHub, you can configure who can see the new repositories that are created
by specifying `visibility` option. Valid options are `public`, `private` and
`internal`. The `internal` option is for GitHub Enterprise clients, which means
public within the enterprise.
```yaml
scaffolder:
github:
visibility: public # or 'internal' or 'private'
```
### Disabling Docker in Docker situation (Optional)
Software Templates use
[Cookiecutter](https://github.com/cookiecutter/cookiecutter) as a templating
library. By default it will use the
[scaffolder-backend/Cookiecutter](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile)
docker image.
If you are running Backstage from a Docker container and you want to avoid
calling a container inside a container, you can set up Cookiecutter in your own
image, this will use the local installation instead.
You can do so by including the following lines in the last step of your
`Dockerfile`:
```Dockerfile
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip3 install cookiecutter
```
+2 -4
View File
@@ -17,10 +17,8 @@ locations like GitHub or GitLab.
### Getting Started
> Be sure to have covered [Installing in your Backstage App](./installation.md)
> for your separate App or
> [Getting Started with Backstage](../../getting-started) for this repo before
> proceeding.
> Be sure to have covered
> [Getting Started with Backstage](../../getting-started) before proceeding.
The Software Templates are available under `/create`. For local development you
should be able to reach them at `http://localhost:3000/create`.
@@ -1,280 +0,0 @@
---
id: installation
title: Installing in your Backstage App
description: Documentation on How to install Backstage App
---
The scaffolder plugin comes in two packages, `@backstage/plugin-scaffolder` and
`@backstage/plugin-scaffolder-backend`. Each has their own installation steps,
outlined below.
The Scaffolder plugin also depends on the Software Catalog. Instructions for how
to set that up can be found [here](../software-catalog/installation.md).
## Installing @backstage/plugin-scaffolder
> **Note that if you used `npx @backstage/create-app`, the plugin may already be
> present**
The scaffolder frontend plugin should be installed in your `app` package, which
is created as a part of `@backstage/create-app`. To install the package, run:
```bash
# From your Backstage root directory
cd packages/app
yarn add @backstage/plugin-scaffolder
```
### Adding the Plugin to your `packages/app`
Add the root page that the Scaffolder plugin provides to your app. You can
choose any path for the route, but we recommend the following:
```tsx
import { ScaffolderPage } from '@backstage/plugin-scaffolder';
// Add to the top-level routes, directly within <FlatRoutes>
<Route path="/create" element={<ScaffolderPage />} />;
```
You may also want to add a link to the template index page to your sidebar:
```tsx
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
// Somewhere within the <Sidebar>
<SidebarItem icon={CreateComponentIcon} to="create" text="Create..." />;
```
This is all that is needed for the frontend part of the Scaffolder plugin to
work!
## Installing @backstage/plugin-scaffolder-backend
> **Note that if you used `npx @backstage/create-app`, the plugin may already be
> present**
The scaffolder backend should be installed in your `backend` package, which is
created as a part of `@backstage/create-app`. To install the package, run:
```bash
# From your Backstage root directory
cd packages/backend
yarn add @backstage/plugin-scaffolder-backend
```
### Adding the Plugin to your `packages/backend`
You'll need to add the plugin to the `backend`'s router. You can do this by
creating a file called `packages/backend/src/plugins/scaffolder.ts` with the
following contents to get you up and running quickly.
```ts
import {
DockerContainerRunner,
SingleHostDiscovery,
} from '@backstage/backend-common';
import {
CookieCutter,
createRouter,
Preparers,
Publishers,
CreateReactAppTemplater,
Templaters,
} from '@backstage/plugin-scaffolder-backend';
import type { PluginEnvironment } from '../types';
import Docker from 'dockerode';
import { CatalogClient } from '@backstage/catalog-client';
export default async function createPlugin({
logger,
config,
database,
reader,
}: PluginEnvironment) {
const dockerClient = new Docker();
const containerRunner = new DockerContainerRunner({ dockerClient });
const cookiecutterTemplater = new CookieCutter({ containerRunner });
const craTemplater = new CreateReactAppTemplater({ containerRunner });
const templaters = new Templaters();
templaters.register('cookiecutter', cookiecutterTemplater);
templaters.register('cra', craTemplater);
const preparers = await Preparers.fromConfig(config, { logger });
const publishers = await Publishers.fromConfig(config, { logger });
const discovery = SingleHostDiscovery.fromConfig(config);
const catalogClient = new CatalogClient({ discoveryApi: discovery });
return await createRouter({
preparers,
templaters,
publishers,
logger,
config,
database,
catalogClient,
reader,
});
}
```
Once the `scaffolder.ts` router setup file is in place, add the router to
`packages/backend/src/index.ts`:
```ts
import scaffolder from './plugins/scaffolder';
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
const apiRouter = Router();
/* several router .use calls */
/* add this line */
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
```
### Adding Templates
At this point the scaffolder backend is installed in your backend package, but
you will not have any templates available to use. These need to be added to the
software catalog, as they are represented as entities of kind
[Template](../software-catalog/descriptor-format.md#kind-template). You can find
out more about adding templates [here](./adding-templates.md).
To get up and running and try out some templates quickly, you can add some of
our example templates through static configuration. Add the following to the
`catalog.locations` section in your `app-config.yaml`:
```yaml
catalog:
locations:
# Backstage Example Templates
- type: url
target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml
- type: url
target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml
- type: url
target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml
```
### Runtime Dependencies / Configuration
For the scaffolder backend plugin to function, you'll need to setup the
integrations config in your `app-config.yaml`.
You can find help for different providers below.
> Note: Some of this configuration may already be set up as part of your
> `app-config.yaml`. We're moving away from the duplicated config for
> authentication in the `scaffolder` section and using `integrations` instead.
#### GitHub
The GitHub access token is retrieved from environment variables via the config.
The config file needs to specify what environment variable the token is
retrieved from. Your config should have the following objects.
You can configure who can see the new repositories that the scaffolder creates
by specifying `visibility` option. Valid options are `public`, `private` and
`internal`. The `internal` option is for GitHub Enterprise clients, which means
public within the enterprise.
```yaml
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
scaffolder:
github:
visibility: public # or 'internal' or 'private'
```
#### GitLab
For GitLab, we currently support the configuration of the GitLab publisher and
allows to configure the private access token and the base URL of a GitLab
instance:
```yaml
integrations:
gitlab:
- host: gitlab.com
token: ${GITLAB_TOKEN}
```
#### Bitbucket
For Bitbucket there are two authentication methods supported. Either `token` or
a combination of `appPassword` and `username`. It looks like either of the
following:
```yaml
integrations:
bitbucket:
- host: bitbucket.org
token: ${BITBUCKET_TOKEN}
```
or
```yaml
integrations:
bitbucket:
- host: bitbucket.org
appPassword: ${BITBUCKET_APP_PASSWORD}
username: ${BITBUCKET_USERNAME}
```
#### Azure DevOps
For Azure DevOps we support both the preparer and publisher stage with the
configuration of a private access token (PAT). For the publisher it's also
required to define the base URL for the client to connect to the service. This
will hopefully support on-prem installations as well but that has not been
verified.
```yaml
integrations:
azure:
- host: dev.azure.com
token: ${AZURE_TOKEN}
```
### Running the Backend
Finally, make sure you have a local Docker daemon running, and start up the
backend with the new configuration:
```bash
cd packages/backend
GITHUB_TOKEN=<token> yarn start
```
If you've also set up the frontend plugin, so you should be ready to go browse
the templates at [localhost:3000/create](http://localhost:3000/create) now!
### Disabling Docker in Docker situation (Optional)
Software Templates use
[Cookiecutter](https://github.com/cookiecutter/cookiecutter) as templating
library. By default it will use the
[spotify/backstage-cookiecutter](https://github.com/backstage/backstage/blob/37e35b910afc7d1270855aed0ec4718aba366c91/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile)
docker image.
If you are running Backstage from a Docker container and you want to avoid
calling a container inside a container, you can set up Cookiecutter in your own
image, this will use the local installation instead.
You can do so by including the following lines in the last step of your
`Dockerfile`:
```Dockerfile
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip3 install cookiecutter
```
@@ -101,9 +101,7 @@ should have something similar to the below in
```ts
return await createRouter({
preparers,
templaters,
publishers,
containerRunner,
logger,
config,
database,
@@ -118,9 +116,7 @@ will set the available actions that the scaffolder has access to.
```ts
const actions = [createNewFileAction()];
return await createRouter({
preparers,
templaters,
publishers,
containerRunner,
logger,
config,
database,
@@ -137,18 +133,17 @@ want to have those as well as your new one, you'll need to do the following:
import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend';
const builtInActions = createBuiltinActions({
containerRunner,
integrations,
config,
catalogClient,
templaters,
reader,
});
const actions = [...builtInActions, createNewFileAction()];
return await createRouter({
preparers,
templaters,
publishers,
containerRunner,
logger,
config,
database,
@@ -4,8 +4,8 @@ title: Writing Templates
description: Details around creating your own custom Software Templates
---
Templates are stored in the **Service Catalog** under a kind `Template`. You can
create your own templates with a small `yaml` definition which describes the
Templates are stored in the **Software Catalog** under a kind `Template`. You
can create your own templates with a small `yaml` definition which describes the
template and it's metadata, along with some input variables that your template
will need, and then a list of actions which are then executed by the scaffolding
service.
@@ -62,7 +62,7 @@ spec:
steps:
- id: fetch-base
name: Fetch Base
action: fetch:cookiecutter
action: fetch:template
input:
url: ./template
values:
@@ -289,8 +289,8 @@ template. These follow the same standard format:
- id: fetch-base # A unique id for the step
name: Fetch Base # A title displayed in the frontend
if: '{{ parameters.name }}' # Optional condition, skip the step if not truthy
action: fetch:cookiecutter # an action to call
input: # input that is passed as arguments to the action handler
action: fetch:template # An action to call
input: # Input that is passed as arguments to the action handler
url: ./template
values:
name: '{{ parameters.name }}'
@@ -317,20 +317,20 @@ output:
### The templating syntax
You might have noticed in the examples that there are `{{ }}`, and these are a
`handlebars` templates for linking and glueing all these different parts of
`yaml` together. All the form inputs from the `parameters` section, when passed
to the steps will be available by using the template syntax
`{{ parameters.something }}`. This is great for passing the values from the form
into different steps and reusing these input variables. To pass arrays or
objects use the syntax `{{ json paramaters.something }}` where
`paramaters.something` is of type `object` or `array` in the `jsonSchema`, such
as the `nicknames` parameter in the previous example.
You might have noticed variables wrapped in `{{ }}` in the examples. These are
`handlebars` template strings for linking and gluing the different parts of the
template together. All the form inputs from the `parameters` section will be
available by using this template syntax (for example,
`{{ parameters.firstName }}` inserts the value of `firstName` from the
parameters). This is great for passing the values from the form into different
steps and reusing these input variables. To pass arrays or objects use the
`json` custom [helper](https://handlebarsjs.com/guide/expressions.html#helpers).
For example, `{{ json parameters.nicknames }}` will insert the result of calling
`JSON.stringify` on the value of the `nicknames` parameter.
As you can see above in the `Outputs` section, `actions` and `steps` can also
output things. So you can grab that output by using
`steps.$stepId.output.$property`.
output things. You can grab that output using `steps.$stepId.output.$property`.
You can read more about all the `inputs` and `outputs` defined in the actions in
code part of the `JSONSchema` or you can read more about our built in ones
code part of the `JSONSchema`, or you can read more about our built in ones
[here](./builtin-actions.md).
@@ -28,10 +28,10 @@ scratch.
### Use the documentation template
Your working Backstage instance should by default have a documentation template
added. If not, follow these
[instructions](../software-templates/installation.md#adding-templates) to add
the documentation template. The template creates a component with only TechDocs
configuration and default markdown files as below mentioned in manual
added. If not, copy the catalog locations from the
[create-app template](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/app-config.yaml.hbs)
to add the documentation template. The template creates a component with only
TechDocs configuration and default markdown files as below mentioned in manual
documentation setup, and is otherwise empty.
![Documentation Template](../../assets/techdocs/documentation-template.png)
-5
View File
@@ -78,11 +78,6 @@ the repository. The archive does not have any git history attached to it. Also
it is a compressed file. Hence the file size is significantly smaller than how
much data git clone has to transfer.
Caveat: Currently TechDocs sites built using URL Reader will be cached for 30
minutes which means they will not be re-built if new changes are made within 30
minutes. This cache invalidation will be replaced by commit timestamp based
implementation very soon.
## How to use a custom TechDocs home page?
### 1st way: TechDocsCustomHome with a custom configuration
+17
View File
@@ -55,3 +55,20 @@ INFO - Start watching changes
[I 210115 19:00:45 handlers:64] Start detecting changes
INFO - Start detecting changes
```
## PlantUML with `svg_object` doesn't render
The [plantuml-markdown](https://pypi.org/project/plantuml-markdown/) MkDocs
plugin available in
[`mkdocs-techdocs-core`](https://github.com/backstage/mkdocs-techdocs-core)
supports different formats for rendering diagrams. TechDocs does however not
support all of them.
The `svg_object` format renders a diagram as an HTML `<object>` tag but this is
not allowed as it enables bad actors to inject malicious content into
documentation pages. See
[CVE-2021-32661](https://github.com/advisories/GHSA-gg96-f8wr-p89f) for more
details.
Instead use `svg_inline` which renders as an `<svg>` tag and provides the same
benefits as `svg_object`.
@@ -370,7 +370,7 @@ techdocs:
openStackSwift:
containerName: 'name-of-techdocs-storage-bucket'
credentials:
userName: ${OPENSTACK_SWIFT_STORAGE_USERNAME}
username: ${OPENSTACK_SWIFT_STORAGE_USERNAME}
password: ${OPENSTACK_SWIFT_STORAGE_PASSWORD}
authUrl: ${OPENSTACK_SWIFT_STORAGE_AUTH_URL}
keystoneAuthVersion: ${OPENSTACK_SWIFT_STORAGE_AUTH_VERSION}
+1 -1
View File
@@ -56,7 +56,7 @@ For example, adding the theme that we created in the previous section can be
done like this:
```ts
import { createApp } from '@backstage/core';
import { createApp } from '@backstage/core-app-api';
const app = createApp({
apis: ...,
+1 -1
View File
@@ -94,7 +94,7 @@ here are some useful ones:
```python
yarn start # Start serving the example app, use --check to include type checks and linting
yarn storybook # Start local storybook, useful for working on components in @backstage/core
yarn storybook # Start local storybook, useful for working on components in @backstage/core-components
yarn workspace @backstage/plugin-welcome start # Serve welcome plugin only, also supports --check
+6 -5
View File
@@ -137,7 +137,7 @@ frontend with `yarn start` in one window, and the backend with
It can often be useful to try out changes to the packages in the main Backstage
repo within your own app. For example if you want to make modifications to
`@backstage/core` and try them out in your app.
`@backstage/core-plugin-api` and try them out in your app.
To link in external packages, add them to your `package.json` and `lerna.json`
workspace paths. These can be either relative or absolute paths with or without
@@ -147,7 +147,7 @@ globs. For example:
"packages": [
"packages/*",
"plugins/*",
"../backstage/packages/core", // New path added to work on @backstage/core
"../backstage/packages/core-plugin-api", // New path added to work on @backstage/core-plugin-api
],
```
@@ -157,9 +157,10 @@ Then reinstall packages to make yarn set up symlinks:
yarn install
```
With this in place you can now modify the `@backstage/core` package within the
main repo, and have those changes be reflected and tested in your app. Simply
run your app using `yarn dev` (or `yarn start` for just frontend) as normal.
With this in place you can now modify the `@backstage/core-plugin-api` package
within the main repo, and have those changes be reflected and tested in your
app. Simply run your app using `yarn dev` (or `yarn start` for just frontend) as
normal.
Note that for backend packages you need to make sure that linked packages are
not dependencies of any non-linked package. If you for example want to work on
@@ -127,18 +127,17 @@ are separated out into their own folder, see further down.
used by the backend, we chose to separate `config` and `config-loader` into
two different packages.
- [`core/`](https://github.com/backstage/backstage/tree/master/packages/core) -
- [`core-app-api/`](https://github.com/backstage/backstage/tree/master/packages/core-app-api) -
This package contains the core APIs that are used to wire together Backstage
apps.
- [`core-components/`](https://github.com/backstage/backstage/tree/master/packages/core-components) -
This package contains our visual React components, some of which you can find
in
[plugin examples](https://backstage.io/storybook/?path=/story/plugins-examples--plugin-with-data).
Apart from that it re-exports everything from [`core-api`] so that users only
need to rely on one package.
- [`core-api/`](https://github.com/backstage/backstage/tree/master/packages/core-api) -
This package contains APIs and definitions of such. It is it's own package
because we needed to split our `test-utils` package. It's an implementation
detail that we try to hide from our users, and no one should have to depend on
it directly.
- [`core-plugin-api/`](https://github.com/backstage/backstage/tree/master/packages/core-plugin-api) -
This package contains the core APIs that are used to build Backstage plugins.
- [`create-app/`](https://github.com/backstage/backstage/tree/master/packages/create-app) -
An CLI to specifically scaffold a new Backstage App. It does so by using a
+2 -2
View File
@@ -6,8 +6,8 @@ sidebar_label: Locations
description: Integrating source code stored in Azure DevOps into the Backstage catalog
---
The Azure integration supports loading catalog entities from Azure DevOps.
Entities can be added to
The Azure DevOps integration supports loading catalog entities from Azure
DevOps. Entities can be added to
[static catalog configuration](../../features/software-catalog/configuration.md),
or registered with the
[catalog-import](https://github.com/backstage/backstage/tree/master/plugins/catalog-import)
+14
View File
@@ -0,0 +1,14 @@
---
id: org
title: Microsoft Azure Active Directory Organizational Data
sidebar_label: Org Data
# prettier-ignore
description: Importing users and groups from a Microsoft Azure Active Directory into Backstage
---
The Backstage catalog can be set up to ingest organizational data - users and
teams - directly from an tenant in Microsoft Azure Active Directory via the
Microsoft Graph API.
More details on this are available in the
[README of the `@backstage/plugin-catalog-backend-module-msgraph` package](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-msgraph/README.md).
+54 -16
View File
@@ -14,19 +14,25 @@ entities that mirror your org setup.
## Installation
The processor that performs the import, `LdapOrgReaderProcessor`, comes
installed with the default setup of Backstage.
1. The processor is not installed by default, therefore you have to add a
dependency to `@backstage/plugin-catalog-backend-module-ldap` to your backend
package.
If you replace the set of processors in your installation using that facility of
the catalog builder class, you can import and add it as follows.
```bash
# From your Backstage root directory
cd packages/backend
yarn add @backstage/plugin-catalog-backend-module-ldap
```
```ts
// Typically in packages/backend/src/plugins/catalog.ts
import { LdapOrgReaderProcessor } from '@backstage/plugin-catalog-backend';
2. The `LdapOrgReaderProcessor` is not registered by default, so you have to
register it in the catalog plugin:
builder.replaceProcessors(
LdapOrgReaderProcessor.fromConfig(config, { logger }),
// ...
```typescript
// packages/backend/src/plugins/catalog.ts
builder.addProcessor(
LdapOrgReaderProcessor.fromConfig(config, {
logger,
}),
);
```
@@ -116,8 +122,8 @@ The DN under which users are stored, e.g.
#### users.options
The search options to use when sending the query to the server, when reading all
users. All of the options are shown below, with their default values, but they
are all optional.
users. All the options are shown below, with their default values, but they are
all optional.
```yaml
options:
@@ -152,8 +158,8 @@ set:
Mappings from well known entity fields, to LDAP attribute names. This is where
you are able to define how to interpret the attributes of each LDAP result item,
and to move them into the corresponding entity fields. All of the options are
shown below, with their default values, but they are all optional.
and to move them into the corresponding entity fields. All the options are shown
below, with their default values, but they are all optional.
If you leave out an optional mapping, it will still be copied using that default
value. For example, even if you do not put in the field `displayName` in your
@@ -198,8 +204,8 @@ The DN under which groups are stored, e.g.
#### groups.options
The search options to use when sending the query to the server, when reading all
groups. All of the options are shown below, with their default values, but they
are all optional.
groups. All the options are shown below, with their default values, but they are
all optional.
```yaml
options:
@@ -276,3 +282,35 @@ map:
# the spec.children field of the entity.
members: member
```
## Customize the Processor
In case you want to customize the ingested entities, the
`LdapOrgReaderProcessor` allows to pass transformers for users and groups.
1. Create a transformer:
```ts
export async function myGroupTransformer(
vendor: LdapVendor,
config: GroupConfig,
group: SearchEntry,
): Promise<GroupEntity | undefined> {
// Transformations may change namespace, change entity naming pattern, fill
// profile with more or other details...
// Create the group entity on your own, or wrap the default transformer
return await defaultGroupTransformer(vendor, config, group);
}
```
2. Configure the processor with the transformer:
```ts
builder.addProcessor(
LdapOrgReaderProcessor.fromConfig(config, {
logger,
groupTransformer: myGroupTransformer,
}),
);
```
+2 -2
View File
@@ -25,7 +25,7 @@ different ways.
The following diagram shows how Backstage might look when deployed inside a
company which uses the Tech Radar plugin, the Lighthouse plugin, the CircleCI
plugin and the service catalog.
plugin and the software catalog.
There are 3 main components in this architecture:
@@ -142,7 +142,7 @@ Its architecture looks like this:
![lighthouse plugin backed to microservice and database](../assets/architecture-overview/lighthouse-plugin-architecture.png)
The service catalog in Backstage is another example of a service backed plugin.
The software catalog in Backstage is another example of a service backed plugin.
It retrieves a list of services, or "entities", from the Backstage Backend
service and renders them in a table for the user.
+2 -2
View File
@@ -22,8 +22,8 @@ Our idea was to centralize and simplify end-to-end software development with an
abstraction layer that sits on top of all of our infrastructure and developer
tooling. Thats Backstage.
Its a developer portal powered by a centralized service catalog — with a plugin
architecture that makes it endlessly extensible and customizable.
Its a developer portal powered by a centralized software catalog — with a
plugin architecture that makes it endlessly extensible and customizable.
Manage all your services, software, tooling, and testing in Backstage. Start
building a new microservice using an automated template in Backstage. Create,
@@ -11,7 +11,7 @@ terminology below for clarity and consistency when discussing Backstage.
### Authentication Glossary
This [page](./auth/glossary.md) directs to the terms and phrases related to
This [page](../auth/glossary.md) directs to the terms and phrases related to
authentication and identity section of Backstage.
### Backstage User Profiles
+3 -3
View File
@@ -22,7 +22,7 @@ We have divided the project into three high-level _phases_:
[UX patterns and components](https://backstage.io/storybook) help ensure a
consistent experience between tools.
- 🐢 **Phase 2:** Service Catalog
- 🐢 **Phase 2:** Software Catalog
([alpha released](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)) -
With a single catalog, Backstage makes it easy for a team to manage ten
services — and makes it possible for your company to manage thousands of them.
@@ -120,13 +120,13 @@ Chances are that someone will jump in and help build it.
- [TechDocs v1](https://backstage.io/blog/2020/09/08/announcing-tech-docs)
- [Plugin marketplace](https://backstage.io/plugins)
- [Improved and move documentation to backstage.io](https://backstage.io/docs/overview/what-is-backstage)
- [Backstage Service Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)
- [Backstage Software Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)
- [Backstage Software Templates (alpha)](https://backstage.io/blog/2020/08/05/announcing-backstage-software-templates)
- [Make it possible to add custom auth providers](https://backstage.io/blog/2020/07/01/how-to-enable-authentication-in-backstage-using-passport)
- [TechDocs v0](https://github.com/backstage/backstage/milestone/15)
- CI plugins: CircleCI, Jenkins, GitHub Actions and TravisCI
- [Service API documentation](https://github.com/backstage/backstage/pull/1737)
- Backstage Service Catalog can read from: GitHub, GitLab,
- Backstage Software Catalog can read from: GitHub, GitLab,
[Bitbucket](https://github.com/backstage/backstage/pull/1938)
- Support auth providers: Google, Okta, GitHub, GitLab,
[auth0](https://github.com/backstage/backstage/pull/1611),
+4 -17
View File
@@ -106,16 +106,6 @@ Used to load in static configuration, mainly for use by the CLI and
Stability: `1`. Mainly intended for internal use.
### `core` [GitHub](https://github.com/backstage/backstage/tree/master/packages/core/)
The `@backstage/core` and `@backstage/core-api` packages are being phased out
and replaced by other `@backstage/core-*` packages. They are still in use but
will not receive any breaking changes.
### `core-api` [GitHub](https://github.com/backstage/backstage/tree/master/packages/core-api/)
Stability: See `@backstage/core` above
### `core-app-api` [GitHub](https://github.com/backstage/backstage/tree/master/packages/core-app-api/)
The APIs used exclusively in the app, such as `createApp` and the system icons.
@@ -194,8 +184,9 @@ Stability: `2`
### `test-utils-core` [GitHub](https://github.com/backstage/backstage/tree/master/packages/test-utils-core/)
Internal testing utilities that are separated out for usage in
@backstage/core-api. All exports are re-exported by @backstage/test-utils. This
package should not be depended on directly.
@backstage/core-app-api and @backstage/core-plugin-api. All exports are
re-exported by @backstage/test-utils. This package should not be depended on
directly.
Stability: See @backstage/test-utils
@@ -218,9 +209,6 @@ Stability: `1`
## Plugins
Plugins are rarely marked as stable as the `@backstage/core` plugin API is under
heavy development.
Many backend plugins are split into "REST API" and "TypeScript Interface"
sections. The "TypeScript Interface" refers to the API used to integrate the
plugin into the backend.
@@ -344,8 +332,7 @@ Stability: `1`
The backend scaffolder plugin that provides an implementation for templates in
the catalog.
Stability: `1`. There is planned work to rework the scaffolder in
https://github.com/backstage/backstage/issues/2771.
Stability: `2`.
### `tech-radar` [GitHub](https://github.com/backstage/backstage/tree/master/plugins/tech-radar/)
+4 -4
View File
@@ -2,13 +2,13 @@
id: what-is-backstage
title: What is Backstage?
# prettier-ignore
description: Backstage is an open platform for building developer portals. Powered by a centralized service catalog, Backstage restores order to your microservices and infrastructure
description: Backstage is an open platform for building developer portals. Powered by a centralized software catalog, Backstage restores order to your microservices and infrastructure
---
![service-catalog](https://backstage.io/blog/assets/6/header.png)
![software-catalog](https://backstage.io/blog/assets/6/header.png)
[Backstage](https://backstage.io/) is an open platform for building developer
portals. Powered by a centralized service catalog, Backstage restores order to
portals. Powered by a centralized software catalog, Backstage restores order to
your microservices and infrastructure and enables your product teams to ship
high-quality code quickly — without compromising autonomy.
@@ -17,7 +17,7 @@ to create a streamlined development environment from end to end.
Out of the box, Backstage includes:
- [Backstage Service Catalog](../features/software-catalog/index.md) for
- [Backstage Software Catalog](../features/software-catalog/index.md) for
managing all your software (microservices, libraries, data pipelines,
websites, ML models, etc.)
+40 -1
View File
@@ -99,7 +99,7 @@ import carmen from './plugins/carmen';
async function main() {
// ...
const carmenEnv = useHotMemoize(module, () => createEnv('carmen'));
apiRouter.use('/carmen', await carmen(badgesEnv));
apiRouter.use('/carmen', await carmen(carmenEnv));
```
After you start the backend (e.g. using `yarn start-backend` from the repo
@@ -111,3 +111,42 @@ curl localhost:7000/api/carmen/health
```
This should return `{"status":"ok"}` like before. Success!
## Making Use of a Database
The Backstage backend comes with a builtin facility for SQL database access.
Most plugins that have persistence needs will choose to make use of this
facility, so that Backstage operators can manage database needs uniformly.
As part of the environment object that is passed to your `createPlugin`
function, there is a `database` field. You can use that to get a
[Knex](http://knexjs.org/) connection object.
```ts
// in packages/backend/src/plugins/carmen.ts
export default async function createPlugin(env: PluginEnvironment) {
const db: Knex<any, unknown[]> = await env.database.getClient();
// You will then pass this client into your actual plugin implementation
// code, maybe similar to the following:
const model = new CarmenDatabaseModel(db);
return await createRouter({
model: model,
logger: env.logger,
});
}
```
You may note that the `getClient` call has no parameters. This is because all
plugin database needs are configured under the `backend.database` config key of
your `app-config.yaml`. The framework may even make sure behind the scenes that
the logical database is created automatically if it doesn't exist, based on
rules that the Backstage operator decides on.
The framework does not handle database schema migrations for you, however. The
builtin plugins in the main repo have chosen to use the Knex library to manage
schema migrations as well, but you can do so in any manner that you see fit.
See the [Knex library documentation](http://knexjs.org/) for examples and
details on how to write schema migrations and perform SQL queries against your
database..
+5 -4
View File
@@ -511,10 +511,11 @@ clarify intent. Refer to the following table to formulate the new name:
## Porting Existing Apps
The first step of porting any app is to replace the root `Routes` component with
`FlatRoutes` from `@backstage/core`. As opposed to the `Routes` component,
`FlatRoutes` only considers the first level of `Route` components in its
children, and provides any additional children to the outlet of the route. It
also removes the need to append `"/*"` to paths, as it is added automatically.
`FlatRoutes` from `@backstage/core-app-api`. As opposed to the `Routes`
component, `FlatRoutes` only considers the first level of `Route` components in
its children, and provides any additional children to the outlet of the route.
It also removes the need to append `"/*"` to paths, as it is added
automatically.
```diff
const AppRoutes = () => (
+4 -4
View File
@@ -22,9 +22,9 @@ yarn create-plugin
This will create a new Backstage Plugin based on the ID that was provided. It
will be built and added to the Backstage App automatically.
> If `yarn start` is already running you should be able to see the default page
> for your new plugin directly by navigating to
> `http://localhost:3000/my-plugin`.
> If the Backstage App is already running (with `yarn start` or `yarn dev`) you
> should be able to see the default page for your new plugin directly by
> navigating to `http://localhost:3000/my-plugin`.
![](../assets/my-plugin_screenshot.png)
@@ -32,7 +32,7 @@ You can also serve the plugin in isolation by running `yarn start` in the plugin
directory. Or by using the yarn workspace command, for example:
```bash
yarn workspace @backstage/plugin-welcome start # Also supports --check
yarn workspace @backstage/my-plugin start # Also supports --check
```
This method of serving the plugin provides quicker iteration speed and a faster
+13
View File
@@ -43,6 +43,10 @@ root of the project which you can then use as an `include` in your
`app-config.yaml`. You can go ahead and
[skip ahead](#including-in-integrations-config) if you've already got an app.
Note that the created app will have a webhook that is disabled by default and
points to `smee.io`, which is intended for local development. There's also
currently no part of Backstage that makes use of the webhook.
### GitHub Enterprise
You have to create the GitHub Application manually using these
@@ -104,3 +108,12 @@ privateKey: |
This will result in backstage preventing the use of any installation that is not
within the allow list.
### Permissions for pull requests
These are the minimum permissions required for creating a pull request with
Backstage software templates:
- Read and Write permissions for `Contents`.
- Read and write permissions for `Pull Requests` and `Issues`.
- Read permissions on `Metadata`.
+4 -4
View File
@@ -28,9 +28,9 @@ This helps the community know what plugins are in development.
You can also use this process if you have an idea for a good plugin but you hope
that someone else will pick up the work.
## Integrate into the Service Catalog
## Integrate into the Software Catalog
If your plugin isn't supposed to live as a standalone page, but rather needs to
be presented as a part of a Service Catalog (e.g. a separate tab or a card on an
"Overview" tab), then check out
[the instruction](integrating-plugin-into-service-catalog.md) on how to do it.
be presented as a part of a Software Catalog (e.g. a separate tab or a card on
an "Overview" tab), then check out
[the instruction](integrating-plugin-into-software-catalog.md) on how to do it.
@@ -1,7 +1,7 @@
---
id: integrating-plugin-into-service-catalog
title: Integrate into the Service Catalog
description: How to integrate a plugin into service catalog
id: integrating-plugin-into-software-catalog
title: Integrate into the Software Catalog
description: How to integrate a plugin into software catalog
---
> This is an advanced use case and currently is an experimental feature. Expect
+3 -3
View File
@@ -36,7 +36,7 @@ to avoid import cycles, for example like this:
```tsx
/* src/routes.ts */
import { createRouteRef } from '@backstage/core';
import { createRouteRef } from '@backstage/core-plugin-api';
// Note: This route ref is for internal use only, don't export it from the plugin
export const rootRouteRef = createRouteRef({
@@ -46,11 +46,11 @@ export const rootRouteRef = createRouteRef({
Now that we have a `RouteRef`, we import it into `src/plugin.ts`, create our
plugin instance with `createPlugin`, as well as create and wrap our routable
extension using `createRoutableExtension` from `@backstage/core`:
extension using `createRoutableExtension` from `@backstage/core-plugin-api`:
```tsx
/* src/plugin.ts */
import { createPlugin, createRouteRef } from '@backstage/core';
import { createPlugin, createRouteRef } from '@backstage/core-plugin-api';
import ExampleComponent from './components/ExampleComponent';
// Create a plugin instance and export this from your plugin package
+4 -1
View File
@@ -57,7 +57,10 @@ package.json to declare the plugin dependencies, metadata and scripts.
In the `src` folder we get to the interesting bits. Check out the `plugin.ts`:
```jsx
import { createPlugin, createRoutableExtension } from '@backstage/core';
import {
createPlugin,
createRoutableExtension,
} from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';
+274
View File
@@ -0,0 +1,274 @@
---
id: url-reader
title: URL Reader
sidebar_label: URL Reader
# prettier-ignore
description: URL Reader is a backend core API responsible for reading files from external locations.
---
## Concept
Some of the core plugins of Backstage have to read files from an external
location. [Software Catalog](../features/software-catalog/index.md) has to read
the [`catalog-info.yaml`](../features/software-catalog/descriptor-format.md)
entity descriptor files to register and track an entity.
[Software Templates](../features/software-templates/index.md) have to download
the template skeleton files before creating a new component.
[TechDocs](../features/techdocs/README.md) has to download the markdown source
files before generating a documentation site.
Since, the requirement for reading files is so essential for Backstage plugins,
the
[`@backstage/backend-common`](https://github.com/backstage/backstage/tree/master/packages/backend-common)
package provides a dedicated API for reading from such URL based remote
locations like GitHub, GitLab, Bitbucket, Google Cloud Storage, etc. This is
commonly referred to as "URL Reader". It takes care of making authenticated
requests to the remote host so that private files can be read securely. If users
have [GitHub App based authentication](github-apps.md) set up, URL Reader even
refreshes the token, to avoid reaching the GitHub API rate limit.
As a result, plugin authors do not have to worry about any of these problems
when trying to read files.
## Interface
When the Backstage backend starts, a new instance of URL Reader is created. You
can see this in the index file of your Backstage backend
i.e.`packages/backend/src/index.ts`.
[Example](https://github.com/backstage/backstage/blob/ebbe91dbe79038a61d35cf6ed2d96e0e0d5a15f3/packages/backend/src/index.ts#L57)
```ts
// File: packages/backend/src/index.ts
import { URLReaders } from '@backstage/backend-common';
function makeCreateEnv(config: Config) {
// ....
const reader = UrlReaders.default({ logger, config });
//
}
```
This instance contains all
[the default URL Reader providers](https://github.com/backstage/backstage/blob/master/packages/backend-common/src/reading/UrlReaders.ts)
in the backend-common package including GitHub, GitLab, Bitbucket, Azure, Google
GCS. As the need arises, more URL Readers are being written to support different
providers.
The generic interface of a URL Reader instance looks like this.
```ts
export type UrlReader = {
/* Used to read a single file and return its content. */
read(url: string): Promise<Buffer>;
/**
* A replacement for the read method that supports options and complex responses.
*
* Use this whenever it is available, as the read method will be deprecated and
* eventually removed in the future.
*/
readUrl?(url: string, options?: ReadUrlOptions): Promise<ReadUrlResponse>;
/* Used to read a file tree and download as a directory. */
readTree(url: string, options?: ReadTreeOptions): Promise<ReadTreeResponse>;
/* Used to search a file in a tree. */
search(url: string, options?: SearchOptions): Promise<SearchResponse>;
};
```
## Using a URL Reader inside a plugin
The `reader` instance is available in the backend plugin environment and passed
on to all the backend plugins. You can see an
[example](https://github.com/backstage/backstage/blob/b0be185369ebaad22255b7cdf18535d1d4ffd0e7/packages/backend/src/plugins/techdocs.ts#L31).
When any of the methods on this instance is called with a URL, URL Reader
extracts the host for that URL (e.g. `github.com`, `ghe.mycompany.com`, etc.).
Using the
[`@backstage/integration`](https://github.com/backstage/backstage/tree/master/packages/integration)
package, it looks inside the
[`integrations:`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/app-config.yaml#L134-L158)
config of the `app-config.yaml` to find out how to work with the host based on
the configs provided like authentication token, API base URL, etc.
Make sure your plugin-specific backend file at
`packages/backend/src/plugins/<PLUGIN>.ts` is forwarding the `reader` instance
passed on as the `PluginEnvironment` to the actual plugin's `createRouter`
function. See how this is done in
[Catalog](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend/src/plugins/catalog.ts#L25-L27)
and
[TechDocs](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend/src/plugins/techdocs.ts#L31-L36)
backend plugins.
Once the reader instance is available inside the plugin, one of its methods can
directly be used with a URL. Some example usages -
- [`read`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/plugins/catalog-backend/src/ingestion/processors/codeowners/read.ts#L24-L33) -
Catalog using the `read` method to read the CODEOWNERS file in a repository.
- [`readTree`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/techdocs-common/src/helpers.ts#L198-L220) -
TechDocs using the `readTree` method to download markdown files in order to
generate the documentation site.
- [`readTree`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/techdocs-common/src/stages/prepare/url.ts#L33-L54) -
TechDocs using `NotModifiedError` to maintain cache and speed up and limit the
number of requests.
- [`search`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.ts#L88-L108) -
Catalog using the `search` method to find files for a location URL containing
a glob pattern.
## Writing a new URL Reader
If the available URL Readers are not sufficient for your use case and you want
to add a new URL Reader for any other provider, you are most welcome to
contribute one!
Feel free to use the
[GitHub URL Reader](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/GithubUrlReader.ts)
as a source of inspiration.
### 1. Add an integration
The provider for your new URL Reader can also be called an "integration" in
Backstage. The `integrations:` section of your Backstage `app-config.yaml`
config file is supposed to be the place where a Backstage integrator defines the
host URL for the integration, authentication details and other integration
related configurations.
The `@backstage/integration` package is where most of the integration specific
code lives, so that it is shareable across Backstage. Functions like "read the
integrations config and process it", "construct headers for authenticated
requests to the host" or "convert a plain file URL into its API URL for
downloading the file" would live in this package.
### 2. Create the URL Reader
Create a new class which implements the
[`UrlReader` type](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/types.ts#L21-L28)
inside `@backstage/backend-common`. Create and export a static `factory` method
which reads the integration config and returns a map of host URLs the new reader
should be used for. See the
[GitHub URL Reader](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/GithubUrlReader.ts#L50-L63)
for example.
### 3. Implement the methods
We want to make sure all URL Readers behave in the same way. Hence if possible,
all the methods of the `UrlReader` interface should be implemented. However it
is okay to start by implementing just one of them and create issues for the
remaining.
#### read
NOTE: Use `readUrl` instead of `read`.
`read` method expects a user-friendly URL, something which can be copied from
the browser naturally when a person is browsing the provider in their browser.
- ✅ Valid URL :
`https://github.com/backstage/backstage/blob/master/ADOPTERS.md`
- ❌ Not a valid URL :
`https://raw.githubusercontent.com/backstage/backstage/master/ADOPTERS.md`
- ❌ Not a valid URL : `https://github.com/backstage/backstage/ADOPTERS.md`
Upon receiving the URL, `read` converts the user-friendly URL into an API URL
which can be used to request the provider's API.
`read` then makes an authenticated request to the provider API and returns the
file's content.
#### readUrl
`readUrl` is a new interface that allows complex response objects and is
intended to replace the `read` method. This new method is currently optional to
implement which allows for a soft migration to `readUrl` instead of `read` in
the future.
#### readTree
`readTree` method also expects user-friendly URLs similar to `read` but the URL
should point to a tree (could be the root of a repository or even a
sub-directory).
- ✅ Valid URL : `https://github.com/backstage/backstage`
- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master`
- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/docs`
Using the provider's API documentation, find out an API endpoint which can be
used to download either a zip or a tarball. You can download the entire tree
(e.g. a repository) and filter out in case the user is expecting only a
sub-tree. But some APIs are smart enough to accept a path and return only a
sub-tree in the downloaded archive.
#### search
`search` method expects a glob pattern of a URL and returns a list of files
matching the query.
- ✅ Valid URL :
`https://github.com/backstage/backstage/blob/master/**/catalog-info.yaml`
- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/**/*.md`
- ✅ Valid URL :
`https://github.com/backstage/backstage/blob/master/*/package.json`
- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/READM`
The core logic of `readTree` can be used here to extract all the files inside
the tree and return the files matching the pattern in the `url`.
### 4. Add to available URL Readers
There are two ways to make your new URL Reader available for use.
You can choose to make it open source, by updating the
[`default` factory](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/UrlReaders.ts#L62-L81)
method of URL Readers.
But for something internal which you don't want to make open source, you can
update your `packages/backend/src/index.ts` file and update how the `reader`
instance is created.
```ts
// File: packages/backend/src/index.ts
const reader = UrlReaders.default({
logger: root,
config,
// This is where your internal URL Readers would go.
factories: [myCustomReader.factory],
});
```
### 5. Caching
All of the methods above support an ETag based caching. If the method is called
without an `etag`, the response contains an ETag of the resource (should ideally
forward the ETag returned by the provider). If the method is called with an
`etag`, it first compares the ETag and returns a `NotModifiedError` in case the
resource has not been modified. This approach is very similar to the actual
[ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) and
[If-None-Match](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match)
HTTP headers.
### 6. Debugging
When debugging one of the URL Readers, you can straightforward use the
[`reader` instance created](https://github.com/backstage/backstage/blob/ebbe91dbe79038a61d35cf6ed2d96e0e0d5a15f3/packages/backend/src/index.ts#L57)
when the backend starts and call one of the methods with your debugging URL.
```ts
// File: packages/backend/src/index.ts
async function main() {
// ...
const createEnv = makeCreateEnv(config);
const testReader = createEnv('test-url-reader').reader;
const response = await testReader.readUrl(
'https://github.com/backstage/backstage/blob/master/catalog-info.yaml',
);
console.log((await response.buffer()).toString());
// ...
}
```
This will be run every time you restart the backend. Note that after any change
in the URL Reader code, you need to kill the backend and restart, since the
`reader` instance is memoized and does not update on hot module reloading. Also,
there are a lot of unit tests written for the URL Readers, which you can make
use of.
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Spotify AB
* Copyright 2020 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.
+2 -2
View File
@@ -11,7 +11,7 @@ can use this to split out logic in your code for manual A/B testing, etc.
Here's a code sample:
```typescript
import { createPlugin } from '@backstage/core';
import { createPlugin } from '@backstage/core-plugin-api';
export default createPlugin({
id: 'plugin-name',
@@ -29,7 +29,7 @@ To inspect the state of a feature flag inside your plugin, you can use the
```tsx
import React from 'react';
import { Button } from '@material-ui/core';
import { featureFlagsApiRef, useApi } from '@backstage/core';
import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api';
const ExamplePage = () => {
const featureFlags = useApi(featureFlagsApiRef);
+1 -1
View File
@@ -30,7 +30,7 @@ type PluginHooks = {
Showcasing adding a feature flag.
```jsx
import { createPlugin } from '@backstage/core';
import { createPlugin } from '@backstage/core-plugin-api';
export default createPlugin({
id: 'new-plugin',
@@ -0,0 +1,204 @@
---
id: configuring-plugin-databases
title: Configuring Plugin Databases
# prettier-ignore
description: Guide on how to configure Backstage databases.
---
This guide covers a variety of production persistence use cases which are
supported out of the box by Backstage. The database manager allows the developer
to set the client and database connection details on a per plugin basis in
addition to the base client and connection configuration. This means that you
can use a SQLite 3 in-memory database for a specific plugin whilst using
PostgreSQL for everything else and so on.
By default, Backstage uses automatically created databases for each plugin whose
names follow the `backstage_plugin_<pluginId>` pattern, e.g.
`backstage_plugin_auth`. You can configure a different database name prefix for
use cases where you have multiple deployments running on a shared database
instance or cluster.
With infrastructure defined as code or data (Terraform, AWS CloudFormation,
etc.), you may have database credentials which lack permissions to create new
databases or you do not have control over the database names. In these
instances, you can set the database name and connection information on a per
plugin basis as mentioned earlier.
Backstage supports all of these use cases with the `DatabaseManager` provided by
`@backstage/backend-common`. We will now cover how to use and configure
Backstage's databases.
## Prerequisites
### Dependencies
Please ensure the appropriate database drivers are installed in your `backend`
package. If you intend to use both `postgres` and `sqlite3`, you can install
both of them.
```sh
cd packages/backend
# install pg if you need postgres
yarn add pg
# install sqlite3 if you intend to set it as the client
yarn add sqlite3
```
From an operational perspective, you only need to install drivers for clients
that are actively used.
### Database Manager
Existing Backstage instances should be updated to use `DatabaseManager` from
`@backstage/backend-common` in your `packages/backend/src/index.ts` file, the
`SingleConnectionDatabaseManager` has been deprecated. Import the manager and
update the references as shown below if this is not the case:
```diff
import {
- SingleConnectionDatabaseManager,
+ DatabaseManager,
} from '@backstage/backend-common';
// ...
function makeCreateEnv(config: Config) {
// ...
- const databaseManager = SingleConnectionDatabaseManager.fromConfig(config);
+ const databaseManager = DatabaseManager.fromConfig(config);
// ...
}
```
## Configuration
You should set the base database client and connection information in your
`app-config.yaml` (or equivalent) file. The base client and configuration is
used as the default which is extended for each plugin with the same or unset
client type. If a client type is specified for a specific plugin which does not
match the base client, the configuration set for the plugin will be used as is
without extending the base configuration.
Client type and configuration for plugins need to be defined under
**`backend.database.plugin.<pluginId>`**. As an example, `catalog` is the
`pluginId` for the catalog plugin and any configuration defined under that block
is specific to that plugin. We will now explore more detailed example
configurations below.
### Minimal In-Memory Configuration
In the example below, we are using `sqlite3` in-memory databases for all
plugins. You may want to use this configuration for testing or other non-durable
use cases.
```yaml
backend:
database:
client: sqlite3
connection: ':memory:'
```
### PostgreSQL
The example below uses PostgreSQL (`pg`) as the database client for all plugins.
The `auth` plugin uses a user defined database name instead of the automatically
generated one which would have been `backstage_plugin_auth`.
```yaml
backend:
database:
client: pg
connection:
host: some.example-pg-instance.tld
user: postgres
password: password
port: 5432
plugin:
auth:
connection:
database: pg_auth_set_by_user
```
### Custom Database Name Prefix
The configuration below uses `example_prefix_` as the database name prefix
instead of `backstage_plugin_`. Plugins such as `auth` and `catalog` will use
databases named `example_prefix_auth` and `example_prefix_catalog` respectively.
```yaml
backend:
database:
client: pg
connection:
host: some.example-pg-instance.tld
user: postgres
password: password
port: 5432
prefix: 'example_prefix_'
```
### Connection Configuration Per Plugin
Both `auth` and `catalog` use connection configuration with different
credentials and database names. This type of configuration can be useful for
environments with infrastructure as code or data which may provide randomly
generated credentials and/or database names.
```yaml
backend:
database:
client: pg
connection: 'postgresql://some.example-pg-instance.tld:5432'
plugin:
auth:
connection: 'postgresql://fort:knox@some.example-pg-instance.tld:5432/unwitting_fox_jumps'
catalog:
connection: 'postgresql://bank:reserve@some.example-pg-instance.tld:5432/shuffle_ransack_playback'
```
### PostgreSQL and SQLite 3
The example below uses PostgreSQL (`pg`) as the database client for all plugins
except the `auth` plugin which uses `sqlite3`. As the `auth` plugin's client
type is different from the base client type, the connection configuration for
`auth` is used verbatim without extending the base configuration for PostgreSQL.
```yaml
backend:
database:
client: pg
connection: 'postgresql://foo:bar@some.example-pg-instance.tld:5432'
plugin:
auth:
client: sqlite3
connection: ':memory:'
```
## Check Your Databases
The `DatabaseManager` will attempt to create the databases if they do not exist.
If you have set credentials per plugin because the credentials in the base
configuration do not have permissions to create databases, you must ensure they
exist before starting the service. The service will not be able to create them,
it can only use them.
### Privileges
As Backstage attempts to check if the database exists, you may need to grant
privileges to list or show databases for a given user. For PostgreSQL, you would
grant the following:
```postgres
GRANT SELECT ON pg_database TO some_user;
```
MySQL:
```mysql
GRANT SHOW DATABASES ON *.* TO some_user;
```
The mechanisms in this guide should help you tackle different database
deployment situations. Good luck!
+5 -5
View File
@@ -55,10 +55,10 @@ const spotifyAuthApiRef = createApiRef<OAuthApi>({
Sam realizes that Spotify auth might be useful to others, and that it would be
more convenient if it was a part of the Backstage Core. After submitting and
merging a Pull Request with the additions to the
`@backstage/plugin-auth-backend` and `@backstage/core` packages, Spotify auth is
now available for everyone to use. Since the Backstage Core team also adds it to
the public demo server, Sam can now get rid of it in the local setup and rely on
the shared development auth providers instead.
`@backstage/plugin-auth-backend` and `@backstage/core-plugin-api` packages,
Spotify auth is now available for everyone to use. Since the Backstage Core team
also adds it to the public demo server, Sam can now get rid of it in the local
setup and rely on the shared development auth providers instead.
The only thing left now is making sure that users of the plugin provide Spotify
auth in the app. Sam ensures this by adding `spotifyAuthApiRef` to the plugin's
@@ -70,7 +70,7 @@ README.
This plugin requires the following APIs to function:
- `spotifyAuthApiRef` from `@backstage/core@^1.1.0`
- `spotifyAuthApiRef` from `@@backstage/core-plugin-api@^1.1.0`
```
# 3. The Catalog Awakens
+135
View File
@@ -0,0 +1,135 @@
---
id: migrating-away-from-core
title: Migrating away from @backstage/core
description: Guide on how to migrate to the new Backstage core libraries.
---
The `@backstage/core` package has been split into three separate packages,
`@backstage/core-app-api`, `@backstage/core-plugin-api`, and
`@backstage/core-components`. For more information about the reasoning behind
this change and the naming of the packages, see the
[original RFC](https://github.com/backstage/backstage/issues/4872) and
[initial PR](https://github.com/backstage/backstage/pull/5825).
The main purpose of the split is to make plugins more decoupled from the app,
and open up for the possibility of combining plugins using many different
versions of the core libraries. This should significantly reduce the maintenance
burden on plugin authors, as well as reduce the impact of breaking changes in
the core APIs.
## Migration
At a high level the migration is done by simply replacing usages of
`@backstage/core` with one or more of the three new core libraries. There are a
few breaking changes in the new packages that are listed below, but for most
plugins the migration is a simple replacement. In order to make the migration as
smooth as possible we provide a collection of tools to automate the majority of
the migration effort.
Below is a list of steps that should get most projects completely migrated, the
order of the steps is a recommendation but not required, so don't worry if you
need to go back to previous steps to fix things.
### Step 1 - Run codemod
The first step is to run
[`@backstage/codemods`](https://www.npmjs.com/package/@backstage/codemods)
across your project. This will automatically convert all module imports in your
source code to use one of the three new core packages instead. For example, the
following change might occur:
```diff
-import { useApi, configApiRef, InfoCard } from '@backstage/core';
+import { useApi, configApiRef } from '@backstage/core-plugin-api';
+import { InfoCard } from '@backstage/core-components';
```
In a typical app created with `@backstage/create-app`, you would run the
following:
```shell
npx @backstage/codemods apply core-imports packages plugins
```
The last two arguments, `packages` and `plugins`, are the folders that the
codemod should be applied to. Add or remove folders as needed for your project.
The codemod might fail for some files because of the missing `IconKey` type in
any of the new packages. This is one of the few breaking changes. To fix, remove
any `IconKey` imports and replace usages of it with the `string` type, see the
breaking changes section below for details. Once usages of `IconKey` type have
been removed, you can re-run the codemod for those files.
Note that while the codemod tries to stick to using the existing formatting in
your project, it doesn't always manage to do that. If you're using `prettier` to
format the code in your project, it's best to run `prettier --write` on any
files that were changed by the codemod.
### Step 2 - Update dependencies
The next step is to update dependencies in your `package.json` files. Any
package that currently depends on `@backstage/core` will need to have it
replaced by one or more of the new packages. The app package should have all
three packages added to `dependencies`, while for plugins and additional non-app
packages, the `@backstage/core-plugin-api` and `@backstage/core-components`
packages should be added to the set of regular `dependencies`, and
`@backstage/core-app-api` should be added to `devDependencies` for usage in
tests.
A tool that can help out with step is the `plugin:diff` command from the
`@backstage/cli`, it will compare your plugin to the base plugin template and
suggest changes where the plugin deviates. A quick way to get this step done if
you have up-to-date project is to run the following in the project root:
```bash
# The --yes flag causes all suggested changes to be accepted automatically
yarn diff --yes
```
If you do not have the `diff` command set up in `package.json`, you can also
manually execute the following in each plugin folder:
```bash
yarn backstage-cli plugin:diff --yes
```
### Step 3 - Manual review
At this point your app is either completely or very close to being migrated. Run
type checks with `yarn tsc` to check if you hit any of the breaking changes
below or if there are any other things to fix. It can also be worthwhile
searching for occurrences of `@backstage/core` in the codebase, as that might
find usages in for example `jest` mock calls, which aren't handled by the
codemod.
As a final step you'll want to boot up the app and take it through any regular
verification step that you have set up for your project. Don't hesitate to open
a GitHub issue, PR, or reach out on Discord if you hit any snags, or if there
are any additional steps or hints that you think should be added to this guide!
## Breaking Changes
The following is a list of breaking changes between `@backstage/core` and the
three new core packages. Not that this list may not be exhaustive depending on
when you migrate your app, as new releases of the new core packages may bring
further changes.
### Removed `IconKey` type
The `IconKey` type used to be a string union of all known keys used for the app
icons available through `useApp().getSystemIcon(key)`. The type has been removed
since the set of allowed icon keys is no longer constrained, and there is
instead only a guarantee that the app provides a minimum set of icons, but can
provide any icons it wants beyond that. Migration is done by simply replacing
old usages by the `string` type.
### Constrained `IconComponent` type
The `IconComponent` type used to allow all of the props from the MUI `SvgIcon`.
This encouraged some bad patterns in open source plugins such as applying colors
to the icons, which in turn hurt the ability to replace the icons with custom
ones. The `IconComponent` type, which is now exported from
`@backstage/core-plugin-api`, now only accepts a `fontSize` prop used to set the
size of the icon. The type is compatible with the MUI `SvgIcon`, but there may
be situations where an icon needs an explicit cast to `IconComponent` in order
to narrow the type.
+4 -11
View File
@@ -72,7 +72,7 @@ Our first modification will be to extract information from the Identity API.
```tsx
// Add identityApiRef to the list of imported from core
import { identityApiRef, useApi } from '@backstage/core';
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
```
3. Adjust the ExampleComponent from inline to block
@@ -137,20 +137,13 @@ changes, let's start by wiping this component clean.
import React from 'react';
import { useAsync } from 'react-use';
import Alert from '@material-ui/lab/Alert';
import {
Table,
TableColumn,
Progress,
githubAuthApiRef,
useApi,
} from '@backstage/core';
import { Table, TableColumn, Progress } from '@backstage/core-components';
import { githubAuthApiRef, useApi } from '@backstage/core-plugin-api';
import { graphql } from '@octokit/graphql';
const ExampleFetchComponent = () => {
export const ExampleFetchComponent = () => {
return <div>Nothing to see yet</div>;
};
export default ExampleFetchComponent;
```
3. Save that and ensure you see no errors. Comment out the unused imports if