Merge branch 'backstage:master' into milliehartnt123-create-ui-doc
This commit is contained in:
+88
-102
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: oidc
|
||||
title: OIDC provider from scratch
|
||||
description: This section shows how to use an OIDC provider from scratch, same steps apply for custom providers.
|
||||
description: This section shows how to enable and use the Backstage OIDC provider.
|
||||
---
|
||||
|
||||
:::info
|
||||
@@ -11,78 +11,65 @@ system, you may want to read [its own article](https://github.com/backstage/back
|
||||
instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)!
|
||||
:::
|
||||
|
||||
This section shows how to use an OIDC provider from scratch, same steps apply for custom
|
||||
providers. Please note these steps are for using a provider, not how to implement one,
|
||||
and Backstage recommends creating custom providers specific to the IDP, so we'll use a
|
||||
`azureOIDC` provider throughout this example, feel free to change any of those refs
|
||||
to your provider name.
|
||||
This section shows how to enable and use the Backstage OIDC provider.
|
||||
|
||||
## Summary
|
||||
|
||||
To add providers not enabled by default like OIDC, we need to follow some steps, we
|
||||
assume you already have a sign-in page to which we'll add the provider so users can
|
||||
sign in through the provider. In simple steps here's how you enable the provider:
|
||||
OIDC is a protocol which has numerous implementations. It's likely that many of your users won't know what the OIDC **protocol** is, but they will recognise your OIDC **implementation**. Backstage supplies a generic `oidc` authorization strategy. You should re-badge this with the name and branding of your OIDC implementation, so that your users will recognise it on the Backstage sign-in page.
|
||||
|
||||
For example, if your organization uses [Keycloak](https://www.keycloak.org), you would re-badge the OIDC provider as `Keycloak` and tell users to `Sign In using Keycloak`.
|
||||
|
||||
## Steps
|
||||
|
||||
The Backstage OIDC provider is not enabled by default. You need to manually enable the provider, and tell it which OIDC server you want to use.
|
||||
|
||||
To enable the Backstage OIDC provider:
|
||||
|
||||
- Create an API reference to identify the provider.
|
||||
- Create the API factory that will handle the authentication.
|
||||
- Add or reuse an auth provider so you can authenticate.
|
||||
- Add or reuse a resolver to handle the result from the authentication.
|
||||
- Configure the provider to access your 3rd party auth solution.
|
||||
- Add the provider to sign in page so users can login with it.
|
||||
- Add the provider to the Backstage sign-in page.
|
||||
|
||||
For simplicity, we assume that you only have a single OIDC provider in your Backstage installation. (If you need to have multiple OIDC providers in Backstage, the steps will be different.)
|
||||
|
||||
We'll explain each step more in detail next.
|
||||
|
||||
### The API reference
|
||||
### The API Reference
|
||||
|
||||
An API reference exist for the sake of **Dependency Injection**, check [Utility APIs][4]
|
||||
for extended explanation.
|
||||
An API reference exists to enable **Dependency Injection**. (See [Utility APIs][4] for an extended explanation.)
|
||||
|
||||
In this OIDC example, we'll create the API reference directly in the
|
||||
`packages/app/src/apis.ts` file, it is not a requirement to put the reference in this
|
||||
file. Any location will do as long as it's available to be imported to where the API
|
||||
factory is, as well as easily accessible to the rest of the application so any package
|
||||
and plugin can inject the API instance when necessary.
|
||||
|
||||
An example of such would be when you use an auth provider from a library installed with
|
||||
NPM, or any other library repository, you would import the API ref from the library.
|
||||
In this example, we'll create the API ref directly in the `packages/app/src/apis.ts` file. It is not a requirement to put the ref in this file. Any location will do as long as it's available to be imported to where the API factory is, as well as easily accessible to the rest of the application so any package and plugin can inject the API instance when necessary.
|
||||
|
||||
```ts
|
||||
export const azureOIDCAuthApiRef: ApiRef<
|
||||
export const keycloakAuthApiRef: ApiRef<
|
||||
OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'auth.my-custom-provider',
|
||||
id: 'auth.keycloak',
|
||||
});
|
||||
```
|
||||
|
||||
Please note a few things, the ID can be anything you want as long as it doesn't conflict
|
||||
with other refs, backstage recommends to use a custom name that references your custom
|
||||
provider, for example we are using OIDC protocol with Azure, so we could use something
|
||||
like `auth.azure.oidc` as well.
|
||||
The `id` of the API ref can be anything you want, as long as it doesn't conflict with other refs. Backstage recommends to use a custom name that references your custom provider.
|
||||
|
||||
Also we're exporting this reference, as well as the `typings`, we need to
|
||||
be able to import this reference anywhere in the app, and the `typings` will tell typescript
|
||||
:::note TypeScript Note
|
||||
As we're exporting this API reference, as well as the TypeScript types, we need to
|
||||
be able to import this reference anywhere in the app. The types will tell TypeScript
|
||||
what instance we're getting from DI when injecting the API. In this case we are defining
|
||||
an API for authentication, so we tell TS that this instance complies with 4 API
|
||||
interfaces:
|
||||
|
||||
- The OICD API that will handle authentication.
|
||||
- The OIDC API that will handle authentication.
|
||||
- Profile API for requesting user profile info from the auth provider in question.
|
||||
- Backstage identity API to handle and associate the user profile with backstage identity.
|
||||
- Session API, to handle the session the user will have while logged in.
|
||||
- Session API, to handle the session the user will have while signed in.
|
||||
:::
|
||||
|
||||
### The API Factory
|
||||
### The API Factory (and auth provider)
|
||||
|
||||
A factory is a function that can take some parameters or dependencies and return an
|
||||
instance of something, in our case it will be a function that requests some backstage
|
||||
APIs and use them to create an instance of an OIDC API provider.
|
||||
The Backstage API factories are part of the Backstage Dependency Injection system. The factory function runs once, when something in your Backstage app first attempts to use an instance of the API it provides. The instance is then cached by the DI system for subsequent lookups.
|
||||
|
||||
Please note that this function only runs (creates the instance) when somewhere else in
|
||||
the app you request the DI to give you an instance of the OIDC provider using the API ref
|
||||
defined above, and the DI will only run this function the first time, from then on any
|
||||
other DI injection will just receive the same instance created the first time, basically
|
||||
the instance is cached by the DI library, a singleton.
|
||||
|
||||
Let's add our OIDC API factory to the APIs array in the `packages/app/src/apis.ts` file:
|
||||
Let's add a new API factory to the `apis` array in the `packages/app/src/apis.ts` file. We will tell it to use the OIDC auth provider internally.
|
||||
|
||||
```ts title="packages/app/src/apis.ts"
|
||||
/* highlight-add-next-line */
|
||||
@@ -91,26 +78,29 @@ import { OAuth2 } from '@backstage/core-app-api';
|
||||
export const apis: AnyApiFactory[] = [
|
||||
/* highlight-add-start */
|
||||
createApiFactory({
|
||||
api: azureOIDCAuthApiRef,
|
||||
api: keycloakAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
// delegate auth to the OAuth2 strategy
|
||||
OAuth2.create({
|
||||
configApi,
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
provider: {
|
||||
id: 'my-auth-provider',
|
||||
title: 'My custom auth provider',
|
||||
// this value MUST be 'oidc'
|
||||
// it maps our Keycloak-branded sign-in provider onto Backstage's generic OIDC auth strategy
|
||||
id: 'oidc',
|
||||
title: 'Keycloak',
|
||||
icon: () => null,
|
||||
},
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
defaultScopes: ['openid', 'profile', 'email'],
|
||||
popupOptions: {
|
||||
// optional, used to customize login in popup size
|
||||
// optional, used to customize sign-in window size
|
||||
size: {
|
||||
fullscreen: true,
|
||||
},
|
||||
@@ -125,27 +115,14 @@ export const apis: AnyApiFactory[] = [
|
||||
}),
|
||||
}),
|
||||
/* highlight-add-end */
|
||||
// ..
|
||||
];
|
||||
```
|
||||
|
||||
Please note we're importing the `OAuth2` class from `@backstage/core-app-api` effectively
|
||||
delegating the authentication to it. Also we're using the `my-auth-provider` ID to tell
|
||||
`OAuth2` to use the auth provider we'll define in the next section, and added the default
|
||||
scopes to request ID, profile, email and user read permissions.
|
||||
|
||||
## The Auth Provider
|
||||
|
||||
The Auth Provider is responsible for authenticating with the 3rd party service, and give
|
||||
us back the credentials, here's where you pick which protocol to use, be it Auth0, OAuth2,
|
||||
OIDC, SAML or any other that your 3rd party IDP provider supports.
|
||||
|
||||
### The Resolver
|
||||
|
||||
Resolvers exist to map user identity from the 3rd party (in this case an azure IDP
|
||||
provider) to the backstage user identity.
|
||||
Resolvers exist to map the user identity from the 3rd party (in this case Keycloak) to the Backstage user identity.
|
||||
|
||||
The default OIDC provider has built-in resolvers, here is how you configure them:
|
||||
The default OIDC provider has a choice of built-in resolvers, here is how you configure them:
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
auth:
|
||||
@@ -159,7 +136,7 @@ auth:
|
||||
- resolver: emailMatchingUserEntityProfileEmail
|
||||
```
|
||||
|
||||
But you can also write a custom resolver as well, see an example below:
|
||||
If none of the built-in resolvers are suitable, you can alternatively write a custom resolver. See an example below:
|
||||
|
||||
```ts title="in packages/backend/src/index.ts"
|
||||
/* highlight-add-start */
|
||||
@@ -174,15 +151,15 @@ const myAuthProviderModule = createBackendModule({
|
||||
// This ID must be exactly "auth" because that's the plugin it targets
|
||||
pluginId: 'auth',
|
||||
// This ID must be unique, but can be anything
|
||||
moduleId: 'my-auth-provider',
|
||||
moduleId: 'keycloak-auth-provider',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: { providers: authProvidersExtensionPoint },
|
||||
async init({ providers }) {
|
||||
providers.registerProvider({
|
||||
// This ID must match the actual provider config, e.g. addressing
|
||||
// auth.providers.azure means that this must be "azure".
|
||||
providerId: 'my-auth-provider',
|
||||
// auth.providers.keycloak means that this must be "keycloak".
|
||||
providerId: 'keycloak',
|
||||
// Use createProxyAuthProviderFactory instead if it's one of the proxy
|
||||
// based providers rather than an OAuth based one
|
||||
factory: createOAuthProviderFactory({
|
||||
@@ -215,76 +192,85 @@ backend.add(myAuthProviderModule);
|
||||
//...
|
||||
```
|
||||
|
||||
For a more a detailed explanation about resolvers check the
|
||||
[Identity Resolver][1] page.
|
||||
For a more detailed explanation about resolvers check the [Identity Resolver][1] page.
|
||||
|
||||
### The configuration
|
||||
### The Configuration
|
||||
|
||||
Since we are using our custom OIDC Auth Provider, we need to add a configuration based
|
||||
on the provider used, in this case based on OIDC protocol (remember the 3rd party has to
|
||||
support the protocol).
|
||||
We will now configure our Keycloak-branded OIDC Auth Provider in Backstage, so that it can talk to our Keycloak server.
|
||||
|
||||
In this example we'll configure OIDC with `my-auth-provider`, to do so we need to
|
||||
[Create app registration][2] in the Azure console, the only difference is that the
|
||||
`http://localhost:7007/api/auth/microsoft/handler/frame` URL needs to change to
|
||||
`http://localhost:7007/api/auth/my-auth-provider/handler/frame`.
|
||||
The first step is to register an OIDC client app for Backstage in your Keycloak server.
|
||||
|
||||
Then we need to configure the env variables for the provider, based on the provider's code
|
||||
in `plugins/auth-backend/src/providers/oidc/provider.ts` we need the following variables
|
||||
in the `app-config.yaml`:
|
||||
Then we need to configure the provider. Based on the provider's code in `plugins/auth-backend/src/providers/oidc/provider.ts` we need the following parameters in the `app-config.yaml`:
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
auth:
|
||||
environment: development
|
||||
### Providing an auth.session.secret will enable session support in the auth-backend
|
||||
session:
|
||||
secret: ${SESSION_SECRET}
|
||||
secret: ${AUTH_SESSION_SECRET}
|
||||
providers:
|
||||
my-auth-provider:
|
||||
oidc:
|
||||
development:
|
||||
metadataUrl: https://example.com/.well-known/openid-configuration
|
||||
clientId: ${AUTH_MY_CLIENT_ID}
|
||||
clientSecret: ${AUTH_MY_CLIENT_SECRET}
|
||||
clientId: ${AUTH_OIDC_CLIENT_ID}
|
||||
clientSecret: ${AUTH_OIDC_CLIENT_SECRET}
|
||||
```
|
||||
|
||||
Anything enclosed in `${}` can be replaced directly in the yaml, or provided as
|
||||
environment variables, the way you obtain all these except `scope` and `prompt` is to
|
||||
check the App Registration you created:
|
||||
Anything enclosed in `${}` can be replaced directly in the YAML, or provided as environment variables.
|
||||
|
||||
#### Required Parameters
|
||||
|
||||
These parameters must always be set.
|
||||
|
||||
- `clientId`: Grab from the Overview page.
|
||||
- `clientSecret`: Can only be seen when creating the secret, if you lose it you'll need a
|
||||
new secret.
|
||||
- `metadataUrl`: In Overview > Endpoints tab, grab OpenID Connect metadata document URL.
|
||||
|
||||
The OIDC provider **also** requires the `auth.session.secret` to be set.
|
||||
|
||||
#### Optional Parameters
|
||||
|
||||
These parameters have implicit default values. Don't override them unless you know what you're doing.
|
||||
|
||||
- `authorizationUrl` and `tokenUrl`: Open the `metadataUrl` in a browser, that json will
|
||||
hold these 2 urls somewhere in there.
|
||||
- `tokenEndpointAuthMethod`: Don't define it, use the default unless you know what it does.
|
||||
- `tokenSignedResponseAlg`: Don't define it, use the default unless you know what it does.
|
||||
- `tokenEndpointAuthMethod`
|
||||
- `tokenSignedResponseAlg`
|
||||
- `scope`: Only used if we didn't specify `defaultScopes` in the provider's factory,
|
||||
basically the same thing.
|
||||
- `prompt`: Recommended to use `auto` so the browser will request login to the IDP if the
|
||||
- `prompt`: Recommended to use `auto` so the browser will request sign-in to the IDP if the
|
||||
user has no active session.
|
||||
- `sessionDuration` (optional): Lifespan of the user session.
|
||||
- `sessionDuration`: Lifespan of the user session.
|
||||
|
||||
Note that for the time being, any change in this yaml file requires a restart of the app,
|
||||
also you need to have the `session.secret` part to use OIDC (some other providers might
|
||||
need this as well) to support user sessions.
|
||||
:::note Config Reloading
|
||||
Backstage does not yet support hot reloading of auth provider configuration. Any changes to this YAML file require a restart of Backstage.
|
||||
:::
|
||||
|
||||
### The Sign In provider
|
||||
### The Sign-In Page
|
||||
|
||||
The last step is to add the provider to the `SignInPage` so users can sign in with your
|
||||
new provider, please follow the [Sign In Configuration][3] docs, here's where you import
|
||||
and use the API reference we defined earlier.
|
||||
The last step is to add the provider to the sign-in page, so users can sign in with your new provider.
|
||||
|
||||
If you are using the standard Backstage [`SignInPage`][3] component, you can just add it to the `providers` array like this:
|
||||
|
||||
```ts title="in packages/app/src/identityProviders.ts"
|
||||
export const providers = [
|
||||
// other providers...
|
||||
{
|
||||
id: 'keycloak-auth-provider',
|
||||
title: 'Keycloak',
|
||||
message: 'Sign In using Keycloak',
|
||||
apiRef: keycloakAuthApiRef,
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
:::note Note
|
||||
|
||||
These steps apply to most if not all the providers, including custom providers, the main
|
||||
difference between different providers will be the contents of the API factory, the code
|
||||
These steps apply to most auth providers. The main
|
||||
difference between providers will be the contents of the API factory, the code
|
||||
in the Auth Provider Factory, the resolver, and the different variables each provider
|
||||
needs in the YAML config or env variables.
|
||||
|
||||
:::
|
||||
|
||||
[1]: https://backstage.io/docs/auth/identity-resolver
|
||||
[2]: https://backstage.io/docs/auth/microsoft/provider#create-an-app-registration-on-azure
|
||||
[3]: https://backstage.io/docs/auth/#sign-in-configuration
|
||||
[4]: https://backstage.io/docs/api/utility-apis
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: root-instance-metadata
|
||||
title: Root Instance Metadata Service
|
||||
sidebar_label: Root Instance Metadata
|
||||
description: Documentation for the Root Instance Metadata service
|
||||
---
|
||||
|
||||
The root instance metadata service provides information about the running Backstage backend instance. Currently, it provides a list of all installed backend plugins.
|
||||
|
||||
:::note Note
|
||||
|
||||
The root instance metadata service only provides information about the specific Backstage instance you're running on. In more complex deployments with multiple Backstage instances, this service will not provide a complete list of all plugins across all instances.
|
||||
|
||||
:::
|
||||
|
||||
## Using the service
|
||||
|
||||
The following example shows how to use the root instance metadata service in your `example` backend plugin to access the list of installed backend plugins.
|
||||
|
||||
```ts
|
||||
import {
|
||||
coreServices,
|
||||
createBackendPlugin,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
createBackendPlugin({
|
||||
pluginId: 'example',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
instanceMetadata: coreServices.rootInstanceMetadata,
|
||||
},
|
||||
async init({ instanceMetadata }) {
|
||||
const plugins = instanceMetadata.getInstalledPlugins();
|
||||
console.log('Installed plugins:', plugins);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Dynamic plugin registration
|
||||
|
||||
The root instance metadata service picks up plugins that are registered at start time through a `backend.start()` call. You need to restart the running backend instance to pick up newly installed plugins.
|
||||
@@ -352,3 +352,69 @@ const extension = createExtension({
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Relative attachment points
|
||||
|
||||
When creating an extension or an [extension blueprint](./23-extension-blueprints.md) you can specify an attachment point that is relative to the current plugin. This is particularly useful for groups of blueprints that are part of a common hierarchy, with extensions from one blueprint attaching to extensions from the other blueprint. For example, the following pair of extension definitions could be installed multiple times in different plugins, each creating their own hierarchy:
|
||||
|
||||
```tsx
|
||||
// Parent extension with a fixed attachment point
|
||||
const parentExtension = createExtension({
|
||||
kind: 'section',
|
||||
attachTo: [{ id: 'app/some-fixed-extension', input: 'children' }],
|
||||
inputs: {
|
||||
content: createExtensionInput([coreExtensionData.reactElement], {
|
||||
singleton: true,
|
||||
}),
|
||||
},
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory({ inputs }) {
|
||||
return [
|
||||
coreExtensionData.reactElement(
|
||||
<section>
|
||||
<h1>Section Title</h1>
|
||||
{inputs.content.get(coreExtensionData.reactElement)}
|
||||
</section>,
|
||||
),
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
// Child extension with a relative attachment point
|
||||
const childExtension = createExtension({
|
||||
kind: 'section-content',
|
||||
attachTo: [{ relative: { kind: 'section' }, input: 'content' }],
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory() {
|
||||
return [coreExtensionData.reactElement(<p>Section Content</p>)];
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Extension input references
|
||||
|
||||
Building on the relative attachment point concept, you can also reference extension inputs directly via the `inputs` property of an extension definition. This provides a more convenient and type-safe way to attach child extensions, especially when using blueprints that provide a nested hierarchy of extensions.
|
||||
|
||||
Extension inputs references are always relative, this means that they can only be used for referencing extensions within the same plugin.
|
||||
|
||||
Each extension definition exposes an `inputs` property that contains references to all of its defined inputs. These references can be passed directly to the `attachTo` option when creating child extensions:
|
||||
|
||||
```tsx
|
||||
const parent = createExtension({
|
||||
inputs: {
|
||||
children: createExtensionInput([coreExtensionData.reactElement]),
|
||||
},
|
||||
// other options...
|
||||
});
|
||||
|
||||
// Create a child extension that attaches to the parent's input
|
||||
const child = createExtension({
|
||||
attachTo: page.inputs.children, // Direct reference to the input
|
||||
output: [coreExtensionData.reactElement], // Outputs are verified against the parent input
|
||||
// other options...
|
||||
});
|
||||
```
|
||||
|
||||
These references are a type-safe way to attach child extensions, it both ensures that the parent input is present, as well as the child providing the required data for the parent.
|
||||
|
||||
Under the hood, input references are resolved in the same way as relative attachment points, using the extension's kind, namespace, and name to construct the final attachment target.
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
---
|
||||
id: custom-theme
|
||||
title: 005 - Customize your App's theme
|
||||
description: Documentation on customizing the look and feel of your Backstage app.
|
||||
---
|
||||
|
||||
Backstage ships with a default theme with a light and dark mode variant. The themes are provided as a part of the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package, which also includes utilities for customizing the default theme, or creating completely new themes.
|
||||
|
||||
## Creating a Custom Theme
|
||||
|
||||
The easiest way to create a new theme is to use the `createUnifiedTheme` function exported by the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. You can use it to override some basic parameters of the default theme such as the color palette and font.
|
||||
|
||||
For example, you can create a new theme based on the default light theme like this:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
}),
|
||||
fontFamily: 'Comic Sans MS',
|
||||
defaultPageTheme: 'home',
|
||||
});
|
||||
```
|
||||
|
||||
:::note Note
|
||||
|
||||
we recommend creating a `theme` folder in `packages/app/src` to place your theme file to keep things nicely organized.
|
||||
|
||||
:::
|
||||
|
||||
You can also create a theme from scratch that matches the `BackstageTheme` type exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). See the
|
||||
[Material UI docs on theming](https://material-ui.com/customization/theming/) for more information about how that can be done.
|
||||
|
||||
## Using your Custom Theme
|
||||
|
||||
To add a custom theme to your Backstage app, you pass it as configuration to `createApp`.
|
||||
|
||||
For example, adding the theme that we created in the previous section can be done like this:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import { UnifiedThemeProvider} from '@backstage/theme';
|
||||
import { myTheme } from './themes/myTheme';
|
||||
|
||||
const app = createApp({
|
||||
apis: ...,
|
||||
plugins: ...,
|
||||
themes: [{
|
||||
id: 'my-theme',
|
||||
title: 'My Custom Theme',
|
||||
variant: 'light',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (
|
||||
<UnifiedThemeProvider theme={myTheme} children={children} />
|
||||
),
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
Note that your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `themes.light` and `themes.dark` from [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
|
||||
|
||||
## Example of a custom theme
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
genPageTheme,
|
||||
palettes,
|
||||
shapes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: {
|
||||
...palettes.light,
|
||||
primary: {
|
||||
main: '#343b58',
|
||||
},
|
||||
secondary: {
|
||||
main: '#565a6e',
|
||||
},
|
||||
error: {
|
||||
main: '#8c4351',
|
||||
},
|
||||
warning: {
|
||||
main: '#8f5e15',
|
||||
},
|
||||
info: {
|
||||
main: '#34548a',
|
||||
},
|
||||
success: {
|
||||
main: '#485e30',
|
||||
},
|
||||
background: {
|
||||
default: '#d5d6db',
|
||||
paper: '#d5d6db',
|
||||
},
|
||||
banner: {
|
||||
info: '#34548a',
|
||||
error: '#8c4351',
|
||||
text: '#343b58',
|
||||
link: '#565a6e',
|
||||
},
|
||||
errorBackground: '#8c4351',
|
||||
warningBackground: '#8f5e15',
|
||||
infoBackground: '#343b58',
|
||||
navigation: {
|
||||
background: '#343b58',
|
||||
indicator: '#8f5e15',
|
||||
color: '#d5d6db',
|
||||
selectedColor: '#ffffff',
|
||||
},
|
||||
},
|
||||
}),
|
||||
defaultPageTheme: 'home',
|
||||
fontFamily: 'Comic Sans MS',
|
||||
/* below drives the header colors */
|
||||
pageTheme: {
|
||||
home: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
documentation: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave2,
|
||||
}),
|
||||
tool: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.round }),
|
||||
service: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
website: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
library: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
other: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
app: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
apis: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For a more complete example of a custom theme including Backstage and Material UI component overrides, see the [Aperture theme](https://github.com/backstage/demo/blob/master/packages/app/src/theme/aperture.ts) from the [Backstage demo site](https://demo.backstage.io).
|
||||
|
||||
## Custom Typography
|
||||
|
||||
When creating a custom theme you can also customize various aspects of the default typography, here's an example using simplified theme:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
typography: {
|
||||
htmlFontSize: 16,
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
h1: {
|
||||
fontSize: 54,
|
||||
fontWeight: 700,
|
||||
marginBottom: 10,
|
||||
},
|
||||
h2: {
|
||||
fontSize: 40,
|
||||
fontWeight: 700,
|
||||
marginBottom: 8,
|
||||
},
|
||||
h3: {
|
||||
fontSize: 32,
|
||||
fontWeight: 700,
|
||||
marginBottom: 6,
|
||||
},
|
||||
h4: {
|
||||
fontWeight: 700,
|
||||
fontSize: 28,
|
||||
marginBottom: 6,
|
||||
},
|
||||
h5: {
|
||||
fontWeight: 700,
|
||||
fontSize: 24,
|
||||
marginBottom: 4,
|
||||
},
|
||||
h6: {
|
||||
fontWeight: 700,
|
||||
fontSize: 20,
|
||||
marginBottom: 2,
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
If you wanted to only override a sub-set of the typography setting, for example just `h1` then you would do this:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
defaultTypography,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
typography: {
|
||||
...defaultTypography,
|
||||
htmlFontSize: 16,
|
||||
fontFamily: 'Roboto, sans-serif',
|
||||
h1: {
|
||||
fontSize: 72,
|
||||
fontWeight: 700,
|
||||
marginBottom: 10,
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## Custom Fonts
|
||||
|
||||
To add custom fonts, you first need to store the font so that it can be imported. We suggest creating the `assets/fonts` directory in your front-end application `src` folder.
|
||||
|
||||
You can then declare the font style following the `@font-face` syntax from [Material UI Typography](https://mui.com/material-ui/customization/typography/).
|
||||
|
||||
After that you can then utilize the `styleOverrides` of `MuiCssBaseline` under components to add a font to the `@font-face` array.
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2';
|
||||
|
||||
const myCustomFont = {
|
||||
fontFamily: 'My-Custom-Font',
|
||||
fontStyle: 'normal',
|
||||
fontDisplay: 'swap',
|
||||
fontWeight: 300,
|
||||
src: `
|
||||
local('My-Custom-Font'),
|
||||
url(${MyCustomFont}) format('woff2'),
|
||||
`,
|
||||
};
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
fontFamily: 'My-Custom-Font',
|
||||
palette: palettes.light,
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
'@font-face': [myCustomFont],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
If you want to utilize different or multiple fonts, then you can set the top level `fontFamily` to what you want for your body, and then override `fontFamily` in `typography` to control fonts for various headings.
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2';
|
||||
import myAwesomeFont from '../assets/fonts/My-Awesome-Font.woff2';
|
||||
|
||||
const myCustomFont = {
|
||||
fontFamily: 'My-Custom-Font',
|
||||
fontStyle: 'normal',
|
||||
fontDisplay: 'swap',
|
||||
fontWeight: 300,
|
||||
src: `
|
||||
local('My-Custom-Font'),
|
||||
url(${MyCustomFont}) format('woff2'),
|
||||
`,
|
||||
};
|
||||
|
||||
const myAwesomeFont = {
|
||||
fontFamily: 'My-Awesome-Font',
|
||||
fontStyle: 'normal',
|
||||
fontDisplay: 'swap',
|
||||
fontWeight: 300,
|
||||
src: `
|
||||
local('My-Awesome-Font'),
|
||||
url(${myAwesomeFont}) format('woff2'),
|
||||
`,
|
||||
};
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
fontFamily: 'My-Custom-Font',
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
'@font-face': [myCustomFont, myAwesomeFont],
|
||||
},
|
||||
},
|
||||
},
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
typography: {
|
||||
...defaultTypography,
|
||||
htmlFontSize: 16,
|
||||
fontFamily: 'My-Custom-Font',
|
||||
h1: {
|
||||
fontSize: 72,
|
||||
fontWeight: 700,
|
||||
marginBottom: 10,
|
||||
fontFamily: 'My-Awesome-Font',
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## Overriding Backstage and Material UI components styles
|
||||
|
||||
When creating a custom theme you would be applying different values to component's CSS rules that use the theme object. For example, a Backstage component's styles might look like this:
|
||||
|
||||
```tsx
|
||||
const useStyles = makeStyles<BackstageTheme>(
|
||||
theme => ({
|
||||
header: {
|
||||
padding: theme.spacing(3),
|
||||
boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)',
|
||||
backgroundImage: theme.page.backgroundImage,
|
||||
},
|
||||
}),
|
||||
{ name: 'BackstageHeader' },
|
||||
);
|
||||
```
|
||||
|
||||
Notice how the `padding` is getting its value from `theme.spacing`, that means that setting a value for spacing in your custom theme would affect this component padding property and the same goes for `backgroundImage` which uses `theme.page.backgroundImage`. However, the `boxShadow` property doesn't reference any value from the theme, that means that creating a custom theme wouldn't be enough to alter the `box-shadow` property or to add css rules that aren't already defined like a margin. For these cases you should also create an override.
|
||||
|
||||
Here's how you would do that:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
}),
|
||||
fontFamily: 'Comic Sans MS',
|
||||
defaultPageTheme: 'home',
|
||||
components: {
|
||||
BackstageHeader: {
|
||||
styleOverrides: {
|
||||
header: ({ theme }) => ({
|
||||
width: 'auto',
|
||||
margin: '20px',
|
||||
boxShadow: 'none',
|
||||
borderBottom: `4px solid ${theme.palette.primary.main}`,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Custom Logo
|
||||
|
||||
In addition to a custom theme, you can also customize the logo displayed at the far top left of the site.
|
||||
|
||||
In your frontend app, locate `src/components/Root/` folder. You'll find two components:
|
||||
|
||||
- `LogoFull.tsx` - A larger logo used when the Sidebar navigation is opened.
|
||||
- `LogoIcon.tsx` - A smaller logo used when the Sidebar navigation is closed.
|
||||
|
||||
To replace the images, you can simply replace the relevant code in those components with raw SVG definitions.
|
||||
|
||||
You can also use another web image format such as PNG by importing it. To do this, place your new image into a new subdirectory such as `src/components/Root/logo/my-company-logo.png`, and then add this code:
|
||||
|
||||
```tsx
|
||||
import MyCustomLogoFull from './logo/my-company-logo.png';
|
||||
|
||||
const LogoFull = () => {
|
||||
return <img src={MyCustomLogoFull} />;
|
||||
};
|
||||
```
|
||||
|
||||
## Icons
|
||||
|
||||
So far you've seen how to create your own theme and add your own logo, in the following sections you'll be shown how to override the existing icons and how to add more icons
|
||||
|
||||
### Custom Icons
|
||||
|
||||
You can also customize the Project's _default_ icons.
|
||||
|
||||
You can change the following [icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx).
|
||||
|
||||
#### Requirements
|
||||
|
||||
- Files in `.svg` format
|
||||
- React components created for the icons
|
||||
|
||||
#### Create React Component
|
||||
|
||||
In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `CustomIcons.tsx` file.
|
||||
|
||||
```tsx title="customIcons.tsx"
|
||||
import { SvgIcon, SvgIconProps } from '@material-ui/core';
|
||||
|
||||
export const ExampleIcon = (props: SvgIconProps) => (
|
||||
<SvgIcon {...props} viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
width="1em"
|
||||
height="1em"
|
||||
display="inline-block"
|
||||
d="M11.6335 10.8398C11.6335 11.6563 12.065 12.9922 13.0863 12.9922C14.1075 12.9922 14.539 11.6563 14.539 10.8398C14.539 10.0234 14.1075 8.6875 13.0863 8.6875C12.065 8.6875 11.6335 10.0234 11.6335 10.8398V10.8398ZM2.38419e-07 8.86719C2.38419e-07 10.1133 0.126667 11.4336 0.692709 12.5781C2.19292 15.5703 6.3175 15.5 9.27042 15.5C12.2708 15.5 16.6408 15.6055 18.2004 12.5781C18.7783 11.4453 19 10.1133 19 8.86719C19 7.23047 18.4498 5.68359 17.3573 4.42969C17.5631 3.8125 17.6621 3.16406 17.6621 2.52344C17.6621 1.68359 17.4681 1.26172 17.0842 0.5C15.291 0.5 14.1431 0.851562 12.7775 1.90625C11.6296 1.63672 10.45 1.51562 9.26646 1.51562C8.19771 1.51562 7.12104 1.62891 6.08396 1.875C4.73813 0.832031 3.59021 0.5 1.81687 0.5C1.42896 1.26172 1.23896 1.68359 1.23896 2.52344C1.23896 3.16406 1.34188 3.80078 1.54375 4.40625C0.455209 5.67188 2.38419e-07 7.23047 2.38419e-07 8.86719V8.86719ZM2.54521 10.8398C2.54521 9.125 3.60208 7.61328 5.45458 7.61328C6.20271 7.61328 6.91917 7.74609 7.67125 7.84766C8.26104 7.9375 8.85083 7.97266 9.45646 7.97266C10.0581 7.97266 10.6479 7.9375 11.2417 7.84766C11.9819 7.74609 12.7063 7.61328 13.4583 7.61328C15.3108 7.61328 16.3677 9.125 16.3677 10.8398C16.3677 14.2695 13.1852 14.7969 10.4144 14.7969H8.50646C5.72375 14.7969 2.54521 14.2734 2.54521 10.8398V10.8398ZM5.81479 8.6875C6.83604 8.6875 7.2675 10.0234 7.2675 10.8398C7.2675 11.6563 6.83604 12.9922 5.81479 12.9922C4.79354 12.9922 4.36208 11.6563 4.36208 10.8398C4.36208 10.0234 4.79354 8.6875 5.81479 8.6875Z"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
```
|
||||
|
||||
#### Using the custom icon
|
||||
|
||||
Supply your custom icon in `packages/app/src/App.tsx`
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
/* highlight-add-next-line */
|
||||
import { ExampleIcon } from './assets/customIcons'
|
||||
|
||||
|
||||
const app = createApp({
|
||||
apis,
|
||||
components: {
|
||||
{/* ... */}
|
||||
},
|
||||
themes: [
|
||||
{/* ... */}
|
||||
],
|
||||
/* highlight-add-start */
|
||||
icons: {
|
||||
github: ExampleIcon,
|
||||
},
|
||||
/* highlight-add-end */
|
||||
bindRoutes({ bind }) {
|
||||
{/* ... */}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Adding Icons
|
||||
|
||||
You can add more icons, if the [default icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx) do not fit your needs, so that they can be used in other places like for Links in your entities. For this example we'll be using icons from[Material UI](https://v4.mui.com/components/material-icons/) and specifically the `AlarmIcon`. Here's how to do that:
|
||||
|
||||
1. First you will want to open your `App.tsx` in `/packages/app/src`
|
||||
2. Then you want to import your icon, add this to the rest of your imports: `import AlarmIcon from '@material-ui/icons/Alarm';`
|
||||
3. Next you want to add the icon like this to your `createApp`:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
const app = createApp({
|
||||
apis: ...,
|
||||
plugins: ...,
|
||||
/* highlight-add-start */
|
||||
icons: {
|
||||
alert: AlarmIcon,
|
||||
},
|
||||
/* highlight-add-end */
|
||||
themes: ...,
|
||||
components: ...,
|
||||
});
|
||||
```
|
||||
|
||||
4. Now we can reference `alert` for our icon in our entity links like this:
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
description: Artist Lookup
|
||||
links:
|
||||
- url: https://example.com/alert
|
||||
title: Alerts
|
||||
icon: alert
|
||||
```
|
||||
|
||||
And this is the result:
|
||||
|
||||

|
||||
|
||||
Another way you can use these icons is from the `AppContext` like this:
|
||||
|
||||
```ts
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
|
||||
const app = useApp();
|
||||
const alertIcon = app.getSystemIcon('alert');
|
||||
```
|
||||
|
||||
You might want to use this method if you have an icon you want to use in several locations.
|
||||
|
||||
:::note Note
|
||||
|
||||
If the icon is not available as one of the default icons or one you've added then it will fall back to Material UI's `LanguageIcon`
|
||||
|
||||
:::
|
||||
|
||||
## Custom Sidebar
|
||||
|
||||
As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar.
|
||||
|
||||
### Sidebar Sub-menu
|
||||
|
||||
For this example we'll show you how you can expand the sidebar with a sub-menu:
|
||||
|
||||
1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives
|
||||
2. Then we want to add the following import for `useApp`:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
```
|
||||
|
||||
3. Then update the `@backstage/core-components` import like this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import {
|
||||
Sidebar,
|
||||
sidebarConfig,
|
||||
SidebarDivider,
|
||||
SidebarGroup,
|
||||
SidebarItem,
|
||||
SidebarPage,
|
||||
SidebarScrollWrapper,
|
||||
SidebarSpace,
|
||||
useSidebarOpenState,
|
||||
Link,
|
||||
/* highlight-add-start */
|
||||
GroupIcon,
|
||||
SidebarSubmenu,
|
||||
SidebarSubmenuItem,
|
||||
/* highlight-add-end */
|
||||
} from '@backstage/core-components';
|
||||
```
|
||||
|
||||
4. Finally replace `<SidebarItem icon={HomeIcon} to="catalog" text="Home" />` with this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
<SidebarItem icon={HomeIcon} to="catalog" text="Home">
|
||||
<SidebarSubmenu title="Catalog">
|
||||
<SidebarSubmenuItem
|
||||
title="Domains"
|
||||
to="catalog?filters[kind]=domain"
|
||||
icon={useApp().getSystemIcon('kind:domain')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Systems"
|
||||
to="catalog?filters[kind]=system"
|
||||
icon={useApp().getSystemIcon('kind:system')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Components"
|
||||
to="catalog?filters[kind]=component"
|
||||
icon={useApp().getSystemIcon('kind:component')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="APIs"
|
||||
to="catalog?filters[kind]=api"
|
||||
icon={useApp().getSystemIcon('kind:api')}
|
||||
/>
|
||||
<SidebarDivider />
|
||||
<SidebarSubmenuItem
|
||||
title="Resources"
|
||||
to="catalog?filters[kind]=resource"
|
||||
icon={useApp().getSystemIcon('kind:resource')}
|
||||
/>
|
||||
<SidebarDivider />
|
||||
<SidebarSubmenuItem
|
||||
title="Groups"
|
||||
to="catalog?filters[kind]=group"
|
||||
icon={useApp().getSystemIcon('kind:group')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Users"
|
||||
to="catalog?filters[kind]=user"
|
||||
icon={useApp().getSystemIcon('kind:user')}
|
||||
/>
|
||||
</SidebarSubmenu>
|
||||
</SidebarItem>
|
||||
```
|
||||
|
||||
When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this:
|
||||
|
||||

|
||||
|
||||
You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar)
|
||||
|
||||
## Custom Homepage
|
||||
|
||||
In addition to a custom theme, a custom logo, you can also customize the
|
||||
homepage of your app. Read the full guide on the [next page](../../getting-started/homepage.md).
|
||||
|
||||
## Migrating to Material UI v5
|
||||
|
||||
We now support Material UI v5 in Backstage. Check out our [migration guide](../../tutorials/migrate-to-mui5.md) to get started.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
id: index
|
||||
title: 'Creating your first Backstage app'
|
||||
---
|
||||
|
||||
### Prerequisites
|
||||
|
||||
None!
|
||||
|
||||
### What should I get out of this guide?
|
||||
|
||||
This guide is the first of 4 Golden Paths that will walk you through everything you need to start working with Backstage. We'll touch on how to get started and spin up a new app, how to write plugins, how to deploy your app to production and how to drive adoption for your new portal. Even if you're non-technical, you can skip forward to the `adoption` Golden Path and learn about how to help your team's new developer portal succeed.
|
||||
|
||||
### Structure
|
||||
|
||||
As mentioned above, this is the first of 4 Golden Paths - you should make sure this guide is 100% complete before continuing on to the other Golden Paths (`adoption` is the exception). We'll start by spinning up a new app for you, walking through what we just created and what you can do after you have a working app.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: installing-plugins
|
||||
sidebar_label: 003 - Installing plugins
|
||||
title: 003 - Installing plugins
|
||||
---
|
||||
|
||||
Now that you have a working Backstage app, let's walk through the most valuable part of the Backstage ecosystem - plugins!
|
||||
|
||||
## What is a Backstage plugin?
|
||||
|
||||
A Backstage plugin usually consists of frontend and backend functionality. Some examples of Backstage plugins are our Software Catalog, Search, and Software Templates plugins! Each plugin provides a series of well-contained focused features, for example - the Software Catalog contains an entity ingestion engine, an optimized query layer for fetching entity information and a series of UI elements that provide list and detail functionality for entities. Some plugins allow modules which supplement existing plugin-level functionality, customizing it for specific use cases - a good example here are catalog processor modules which allow for ingesting data from common sources into the catalog.
|
||||
|
||||
:::note Backstage Plugin Naming
|
||||
|
||||
The `backstage-cli new` command scaffolds plugins automatically with the expected naming conventions. We describe the naming conventions below for users who are installing external plugins.
|
||||
|
||||
:::
|
||||
|
||||
You'll generally have multiple packages that combine into a single "plugin". The common naming standard (detailed in [ADR-11](../../architecture-decisions/adr011-plugin-package-structure.md)) is demonstrated below for plugin `x`:
|
||||
|
||||
> `x`: Primary frontend entrypoint, contains frontend-only code for the plugin.
|
||||
|
||||
> `x-backend`: Primary backend entrypoint, contains backend-only code.
|
||||
|
||||
> `x-backend-module-y`: Optional backend module `y` for plugin `x`, contains backend-only code.
|
||||
|
||||
> `x-node`: Shared utilities for consumers of backend plugin `x`, should _NOT_ be used on the frontend.
|
||||
|
||||
> `x-react`: Shared utilities for consumers of frontend plugin `x`, should _NOT_ be used on the backend.
|
||||
|
||||
> `x-common`: Shared utilities for consumers of plugin `x`, can be used across backend and frontend.
|
||||
|
||||
Not all plugins need all of those packages, we recommend starting with just a `x` and `x-backend` package and expanding from there.
|
||||
|
||||
## How do I install a plugin?
|
||||
|
||||
As mentioned above, there's 2 parts to installing a plugin - the frontend and the backend. It's recommended to start with installing the backend plugin to ensure your frontend doesn't run into any weird errors.
|
||||
|
||||
In both cases, you'll want to find the plugin's installation documentation. For most plugins, this is available through that plugin's `README.md` file. For example, the Software Catalog plugin's installation instructions are available through their [backend plugin README](https://github.com/backstage/backstage/blob/850ad502eafc356d940e4f1ce6d32951548bb257/plugins/catalog-backend/README.md#L1) and [frontend plugin README](https://github.com/backstage/backstage/blob/850ad502eafc356d940e4f1ce6d32951548bb257/plugins/catalog/README.md#L1).
|
||||
|
||||
### Installing a Backend Plugin
|
||||
|
||||
Generally, installing a backend plugin is really easy - you just add a
|
||||
|
||||
```
|
||||
backend.import(`@scope/package`)
|
||||
```
|
||||
|
||||
to your `packages/backend/src/index.ts` file alongside the other entries. Saving the file will trigger a hot reload and just like that your new plugin is available and usable. For advanced cases, there may be required config for the plugin that you'll have to set. That config will (or should) be documented by the plugin in their `README`.
|
||||
|
||||
You may also need to add backend modules to provide the additional functionality in the plugin that you're looking for. Backend modules are further extensions to backend code that can provide tailored functionality, good examples are catalog processor modules that add support for Github, LDAP and AWS software entities. Modules install the exact same way as backend plugins. Installing a module may also require additional configuration, which should also be documented in the plugin's `README`.
|
||||
|
||||
### Installing a Frontend Plugin
|
||||
|
||||
Frontend plugins have multiple entrypoints, you should follow the plugin's documentation for how to install it.
|
||||
|
||||
The New Frontend System vastly simplifies this! Keep your eyes peeled for updates.
|
||||
|
||||
## Finding plugins
|
||||
|
||||
The open source community already has a host of plugins that solve many common asks - we recommend you look through [the plugin directory](https://backstage.io/plugins) before you go about creating your own!
|
||||
|
||||
You can find other community maintained plugins in the [Community Plugins Repository](https://github.com/backstage/community-plugins)!
|
||||
|
||||
## Next Steps
|
||||
|
||||
If you're chomping at the bit to write your own plugin, you can move to the `plugins` Golden Path. We recommend you make a note to come back and finish this Golden Path to get more information on maintaining a Backstage app long term.
|
||||
|
||||
For the rest of you, let's walk through keeping your Backstage app up to date!
|
||||
@@ -0,0 +1,166 @@
|
||||
---
|
||||
id: keeping-backstage-updated
|
||||
sidebar_label: 006 - Keep Backstage updated
|
||||
title: 006 - Keeping Backstage up to date
|
||||
---
|
||||
|
||||
Audience: Developers and Admins
|
||||
|
||||
:::note Note
|
||||
To better understand the concepts in this section, it's recommended to have an understanding of [Monorepos](https://semaphoreci.com/blog/what-is-monorepo), [Semantic Versioning](https://semver.org) and [CHANGELOGs](https://keepachangelog.com).
|
||||
:::
|
||||
|
||||
## Summary
|
||||
|
||||
Backstage is always improving, so it's a good idea to stay in sync with the
|
||||
latest releases. Backstage is more of a library than an application or service;
|
||||
similar to `create-react-app`, the `@backstage/create-app` tool gives you a
|
||||
starting point that's meant to be evolved.
|
||||
|
||||
## Updating Backstage versions with backstage-cli
|
||||
|
||||
The Backstage CLI has a command to bump all `@backstage` packages and
|
||||
dependencies you're using to the latest versions:
|
||||
[versions:bump](https://backstage.io/docs/tooling/cli/03-commands#versionsbump).
|
||||
|
||||
```bash
|
||||
yarn backstage-cli versions:bump
|
||||
```
|
||||
|
||||
The reason for bumping all `@backstage` packages at once is to maintain the
|
||||
dependencies that they have between each other.
|
||||
|
||||
<a name="plugin"></a>
|
||||
:::tip
|
||||
|
||||
To make the version bump process even easier and more streamlined we highly recommend using the [Backstage yarn plugin](#managing-package-versions-with-the-backstage-yarn-plugin)
|
||||
|
||||
:::
|
||||
|
||||
By default the bump command will upgrade `@backstage` packages to the latest `main` release line which is released monthly. For those in a hurry that want to track the `next` release line which releases weekly can do so using the `--release next` option.
|
||||
|
||||
```bash
|
||||
yarn backstage-cli versions:bump --release next
|
||||
```
|
||||
|
||||
If you are using other plugins you can pass in the `--pattern` option to update
|
||||
more than just the `@backstage/*` dependencies.
|
||||
|
||||
```bash
|
||||
yarn backstage-cli versions:bump --pattern '@{backstage,roadiehq}/*'
|
||||
```
|
||||
|
||||
## Following create-app template changes
|
||||
|
||||
The `@backstage/create-app` command creates the initial structure of your
|
||||
Backstage installation from a **template**. The source of this template in the
|
||||
Backstage repository is updated periodically, but your local `app` and `backend`
|
||||
packages are established at `create-app` time and won't automatically get these
|
||||
template updates.
|
||||
|
||||
For this reason, any changes made to the template are documented along with
|
||||
upgrade instructions in the
|
||||
[changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md)
|
||||
of the `@backstage/create-app` package. We recommend peeking at this changelog
|
||||
for any applicable updates when upgrading packages. As an alternative, the
|
||||
[Backstage Upgrade Helper](https://backstage.github.io/upgrade-helper/) provides
|
||||
a consolidated view of all the changes between two versions of Backstage. You
|
||||
can find the current version of your Backstage installation in `backstage.json` located in the root of your backstage repository.
|
||||
|
||||
## Managing package versions with the Backstage yarn plugin
|
||||
|
||||
The Backstage yarn plugin makes it easier to manage Backstage package versions,
|
||||
by determining the appropriate version for each package based on the overall
|
||||
Backstage version in `backstage.json`. This avoids the need to update every
|
||||
package.json across your Backstage monorepo, and means that when adding new
|
||||
`@backstage` dependencies, you don't need to worry about figuring out the right
|
||||
version to use to match the currently-installed release of Backstage.
|
||||
|
||||
### Requirements
|
||||
|
||||
In order to use the yarn plugin, you'll need to be using yarn 4.1.1 or greater.
|
||||
|
||||
### Installation
|
||||
|
||||
To install the yarn plugin, run the following command in your Backstage
|
||||
monorepo:
|
||||
|
||||
```bash
|
||||
yarn plugin import https://versions.backstage.io/v1/tags/main/yarn-plugin
|
||||
```
|
||||
|
||||
The resulting changes in the file system should be committed to your repo.
|
||||
|
||||
:::tip
|
||||
|
||||
For best results it's ideal to add the Backstage Yarn plugin when you are about to do a Backstage upgrade as it will make it easier to confirm everything is working.
|
||||
|
||||
:::
|
||||
|
||||
### Usage
|
||||
|
||||
When the yarn plugin is installed, versions for currently-released `@backstage`
|
||||
packages can be replaced in package.json with the string `"backstage:^"`. This
|
||||
instructs yarn to resolve the version based on the overall Backstage version in
|
||||
`backstage.json`.
|
||||
|
||||
:::tip
|
||||
|
||||
The `backstage.json` is key for the plugin to work, make sure this file is included in your CI/CD pipelines and/or any Container builds.
|
||||
|
||||
:::
|
||||
|
||||
The `backstage-cli versions:bump` command documented above will detect the
|
||||
installation of the yarn plugin, and when it's installed, will automatically
|
||||
migrate dependencies across the monorepo to use it.
|
||||
|
||||
## More information on dependency mismatches
|
||||
|
||||
Backstage is structured as a monorepo with
|
||||
[Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/). This means
|
||||
the `app` and `backend` packages, as well as any custom plugins you've added,
|
||||
are separate packages with their own `package.json` and dependencies.
|
||||
|
||||
When a given dependency version is the _same_ between different packages, the
|
||||
dependency is hoisted to the main `node_modules` folder in the monorepo root to
|
||||
be shared between packages. When _different_ versions of the same dependency are
|
||||
encountered, Yarn creates a `node_modules` folder within a particular package.
|
||||
This can lead to multiple versions of the same package being installed and used
|
||||
in the same app.
|
||||
|
||||
All Backstage core packages are implemented in such as way that package
|
||||
duplication is **not** a problem. For example, duplicate installations of
|
||||
packages like `@backstage/core-plugin-api`, `@backstage/core-components`,
|
||||
`@backstage/plugin-catalog-react`, and `@backstage/backend-plugin-api` are all
|
||||
acceptable.
|
||||
|
||||
While package duplication might be acceptable in many cases, you might want to
|
||||
deduplicate packages for the purpose of optimizing bundle size and installation
|
||||
speed. We recommend using deduplication utilities such as `yarn dedupe` to trim
|
||||
down the number of duplicate packages.
|
||||
|
||||
## Proxy
|
||||
|
||||
The Backstage CLI uses [global-agent](https://www.npmjs.com/package/global-agent) and `undici` to configure HTTP/HTTPS proxy settings using environment variables. This allows you to route the CLI’s network traffic through a proxy server, which can be useful in environments with restricted internet access.
|
||||
|
||||
Additionally, `yarn` needs a proxy too (sometimes), when in environments with restricted internet access. It uses different settings than the other modules. If you decide to use the backstage yarn plugin [mentioned above](#plugin), you will need to set additional proxy values.
|
||||
If you will always need proxy settings in all environments and situations, you can add `httpProxy` and `httpsProxy` values to [the yarnrc.yml file](https://yarnpkg.com/configuration/yarnrc). If some environments need it (say a developer workstation) but other environments do not (perhaps a CI build server running on AWS), then you may not want to update the yarnrc.yml file but just set environment variables `YARN_HTTP_PROXY` and `YARN_HTTPS_PROXY` in the environments/situations where you need to proxy.
|
||||
|
||||
**If you plan to use the backstage yarn plugin, you will need these extra yarn proxy settings to both install the plugin and run the `versions:bump` command**. If you do not plan to use the backstage yarn plugin, it seems like the global agent proxy settings alone are sufficient.
|
||||
|
||||
### Example Configuration
|
||||
|
||||
```bash
|
||||
export HTTP_PROXY=http://proxy.company.com:8080
|
||||
export HTTPS_PROXY=https://secure-proxy.company.com:8080
|
||||
export NO_PROXY=localhost,internal.company.com
|
||||
export GLOBAL_AGENT_HTTP_PROXY=${HTTP_PROXY}
|
||||
export GLOBAL_AGENT_HTTPS_PROXY=${HTTPS_PROXY}
|
||||
export GLOBAL_AGENT_NO_PROXY=${NO_PROXY}
|
||||
export YARN_HTTP_PROXY=${HTTP_PROXY} # optional
|
||||
export YARN_HTTPS_PROXY=${HTTPS_PROXY} # optional
|
||||
```
|
||||
|
||||
## Rollback migrations
|
||||
|
||||
In some cases you could need to downgrade Backstage instance due to some problem or maybe because you are using a test environment to validate the new version of Backstage. You can check the [Manual Rollback using Knex](../../tutorials/manual-knex-rollback.md) guide to know how to rollback migrations using Knex.
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: local-development
|
||||
title: 002 - Local development
|
||||
---
|
||||
|
||||
Your Backstage app is fully installed and ready to be run! Now that the installation is complete, you can go to the application directory and start the app using the `yarn start` command. The `yarn start` command will run both the frontend and backend as separate processes (named `[0]` and `[1]`) in the same window.
|
||||
|
||||
```bash
|
||||
cd my-backstage-app # your app name
|
||||
yarn start
|
||||
```
|
||||
|
||||

|
||||
|
||||
Here again, there's a small wait for the frontend to start up. Once the frontend is built, your browser window should automatically open.
|
||||
|
||||
:::tip Browser window didn't open
|
||||
|
||||
When you see the message `[0] webpack compiled successfully`, you can navigate directly to `http://localhost:3000` to see your Backstage app.
|
||||
|
||||
:::
|
||||
|
||||
Once its spun up, you should see something similar to the below.
|
||||
|
||||

|
||||
|
||||
## Architecture of local development
|
||||
|
||||
:::note Deploy architecture
|
||||
|
||||
This section only touches on local development, we'll walk through what a Golden Path production architecture looks like in the `deployment` Golden Path.
|
||||
|
||||
:::
|
||||
|
||||
Now that you have that running, let's talk through what you just set up. You have 2 commands running as part of `yarn start` - the website that is stored at `packages/app` and the backend stored at `packages/backend`.
|
||||
|
||||
The website listens on port `3000` by default. It's a React app with some extra flavor to provide strong plugin-friendly defaults. For local development, we use `rspack` for fast compilation and near-instant feedback.
|
||||
|
||||
The backend listens on port `7007` by default. It is a NodeJS app that has among other things an HTTP server through `express` and talks to a database.
|
||||
|
||||
Locally, we use `sqlite` for the database. This is a fast in-memory database that is perfect for local development. Because of its ephemeral nature, you shouldn't rely on the database to keep data across `yarn start`s. We _do_ however, maintain the database across hot reloads.
|
||||
|
||||
Speaking of hot reloads, these are supported for both the frontend and backend.
|
||||
|
||||
In the frontend, whenever you save a file used by your React app, after a slight delay, you should see a message like
|
||||
|
||||
```
|
||||
Rspack compiled successfully
|
||||
```
|
||||
|
||||
In the backend, you should see a
|
||||
|
||||
```
|
||||
Change detected, restarting the development server...
|
||||
```
|
||||
|
||||
followed by init logs from your server.
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
id: logging-in
|
||||
title: 004 - Logging into your instance
|
||||
description: Getting up and running with Backstage and your identity provider
|
||||
---
|
||||
|
||||
Audience: Developers, Admins
|
||||
|
||||
## Summary
|
||||
|
||||
This guide will provide a quick tutorial on how to log in to your Backstage instance. It should be used as both an introduction to Backstage's authentication system as well as a debugging guide for any issues you may have while logging in.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You should have completed the GitHub OAuth app setup defined in [the authentication tutorial](../../getting-started/config/authentication.md).
|
||||
|
||||
## 1. Login to Backstage
|
||||
|
||||
Run your Backstage app with `yarn start`. Navigate to `http://localhost:3000`.
|
||||
|
||||
If you're not already logged in, you should see a login screen like this,
|
||||
|
||||

|
||||
|
||||
To login, you should choose the "GitHub" provider and click the "Sign in" button. This will redirect you to a GitHub OAuth page. Verify that the scopes mentioned on that page match the setup you did in [the authentication tutorial](../../getting-started/config/authentication.md). Once you click "Confirm", you will be brought back to the Backstage interface and signed in!
|
||||
|
||||
If you are already logged in, you will be automatically brought to your Backstage instance.
|
||||
|
||||
## 2. Verify that you're logged in
|
||||
|
||||
Once you've logged in, find the "Settings" item in the navigation bar to the left. Click it and you will see your profile. If you see your profile picture and name from GitHub here, congratulations! You've successfully set up a GitHub authentication integration.
|
||||
|
||||
<!-- Would like to have more FAQs here for help instead of funneling to Discord -->
|
||||
|
||||
If you don't see your profile picture and name, check that you followed all of the steps in [the authentication tutorial](../../getting-started/config/authentication.md). If you have, search for similar issues on [the Discord server](https://discord.gg/backstage-687207715902193673).
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
id: npx-create-app
|
||||
title: '001 - Scaffolding'
|
||||
---
|
||||
|
||||
Audience: Developers and Admins
|
||||
|
||||
:::note Note
|
||||
It is not required, although recommended to have a basic understanding of [Yarn](https://www.pluralsight.com/guides/yarn-a-package-manager-for-node-js) and [npm](https://docs.npmjs.com/about-npm) before starting this guide.
|
||||
:::
|
||||
|
||||
## Summary
|
||||
|
||||
This guide walks through how to get started creating your very own Backstage customizable app. This is the first step in evaluating, developing on, or demoing Backstage.
|
||||
|
||||
By the end of this guide, you will have a standalone Backstage installation running locally with a `SQLite` database and demo content.
|
||||
|
||||
:::caution Organization customization
|
||||
|
||||
To be clear, this is not a production-ready installation, and it does not contain information specific to your organization. You will learn how to customize Backstage for your use case through this guide.
|
||||
|
||||
:::
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This guide also assumes a basic understanding of working on a Linux based operating system and have some experience with the terminal, specifically, these commands: `npm`, `yarn`.
|
||||
|
||||
- Access to a Unix-based operating system, such as Linux, macOS or
|
||||
[Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/)
|
||||
- A GNU-like build environment available at the command line.
|
||||
For example, on Debian/Ubuntu you will want to have the `make` and `build-essential` packages installed.
|
||||
On macOS, you will want to have run `xcode-select --install` to get the XCode command line build tooling in place.
|
||||
- An account with elevated rights to install the dependencies
|
||||
- `curl` or `wget` installed
|
||||
- Node.js [Active LTS Release](../../overview/versioning-policy.md#nodejs-releases) installed using one of these
|
||||
methods:
|
||||
- Using `nvm` (recommended)
|
||||
- [Installing nvm](https://github.com/nvm-sh/nvm#install--update-script)
|
||||
- [Install and change Node version with nvm](https://nodejs.org/en/download/package-manager/#nvm)
|
||||
- Node 20 is a good starting point, this can be installed using `nvm install lts/iron`
|
||||
- [Binary download](https://nodejs.org/en/download/)
|
||||
- [Package manager](https://nodejs.org/en/download/package-manager/)
|
||||
- [Using NodeSource packages](https://github.com/nodesource/distributions/blob/master/README.md)
|
||||
- `yarn` [Installation](https://yarnpkg.com/getting-started/install)
|
||||
- Backstage currently uses Yarn 4.4.1, once you've ran `corepack enable` you'll want to then run `yarn set version 4.4.1`
|
||||
- `git` [installation](https://github.com/git-guides/install-git)
|
||||
|
||||
## Scaffold your new Backstage app
|
||||
|
||||
## 1. Create your Backstage App
|
||||
|
||||
To scaffold your new Backstage app, we'll be running an interactive command. Before you run the command, you should open a terminal and move your current working directory somewhere you're comfortable creating a new directory.
|
||||
|
||||
The wizard for this command will ask what name you want to have for your new app. That name will match the folder that we create for you.
|
||||
|
||||
When you run the command, you'll see an output like this.
|
||||
|
||||

|
||||
|
||||
And when it finishes, you'll have a working Backstage app (with example data)!
|
||||
|
||||
Now, that we know what it does, let's actually scaffold some code!
|
||||
|
||||
```bash
|
||||
npx @backstage/create-app@latest
|
||||
```
|
||||
|
||||
This may take a few minutes to fully install everything. Don't stress if the loading seems to be spinning nonstop, there's a lot going on in the background.
|
||||
|
||||
:::note
|
||||
|
||||
If this fails on the `yarn install` step, it's likely that you will need to install some additional dependencies which are used to configure `isolated-vm`. You can find out more in their [requirements section](https://github.com/laverdet/isolated-vm#requirements), and then run `yarn install` manually again after you've completed those steps.
|
||||
|
||||
:::
|
||||
|
||||
## Structure of your app
|
||||
|
||||
### General folder structure
|
||||
|
||||
Below is a simplified layout of the files and folders generated when creating an app.
|
||||
|
||||
```
|
||||
app
|
||||
├── app-config.yaml
|
||||
├── catalog-info.yaml
|
||||
├── package.json
|
||||
└── packages
|
||||
├── app
|
||||
└── backend
|
||||
```
|
||||
|
||||
- **app-config.yaml**: Main configuration file for the app. See
|
||||
[Configuration](https://backstage.io/docs/conf/) for more information.
|
||||
- **catalog-info.yaml**: Catalog Entities descriptors. See
|
||||
[Descriptor Format of Catalog Entities](https://backstage.io/docs/features/software-catalog/descriptor-format)
|
||||
to get started.
|
||||
- **package.json**: Root package.json for the project. _Note: Be sure that you
|
||||
don't add any npm dependencies here as they probably should be installed in
|
||||
the intended workspace rather than in the root._
|
||||
- **packages/**: Lerna leaf packages or "workspaces". Everything here is going
|
||||
to be a separate package, managed by lerna.
|
||||
- **packages/app/**: A fully functioning Backstage frontend app that acts as a
|
||||
good starting point for you to get to know Backstage.
|
||||
- **packages/backend/**: We include a backend that helps power features such as
|
||||
[Authentication](https://backstage.io/docs/auth/),
|
||||
[Software Catalog](https://backstage.io/docs/features/software-catalog/),
|
||||
[Software Templates](https://backstage.io/docs/features/software-templates/)
|
||||
and [TechDocs](https://backstage.io/docs/features/techdocs/)
|
||||
amongst other things.
|
||||
|
||||
## Common Issues
|
||||
|
||||
- App is not running on port X: Backstage uses ports `3000` and `7007` as its default frontend and backend ports. Make sure that your commands haven't exited with errors. For remote or containerized setups, make sure those ports above are accessible.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you have a scaffolded app, let's learn how to start it locally for development!
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
id: 001-first-steps
|
||||
sidebar_label: 001 - Scaffolding the plugin
|
||||
title: How to scaffold a new plugin?
|
||||
---
|
||||
|
||||
# Scaffolding a new plugin
|
||||
|
||||
<!-- Talk through how to run the `backstage-cli create` command as well as what the output it creates is. This should touch on why we install this into `packages/backend`. -->
|
||||
|
||||
## `yarn new`
|
||||
|
||||
A new, bare-bones backend plugin package can be created by issuing the following
|
||||
command in your Backstage repository's root directory and selecting `backend-plugin`:
|
||||
|
||||
```sh
|
||||
yarn new
|
||||
```
|
||||
|
||||
You will be asked to supply a name for the plugin. This is an identifier that
|
||||
will be part of the NPM package name, so make it short and containing only
|
||||
lowercase characters separated by dashes, for our example, you should provide `todo`. For plugins you may write in the future, this should be an easy to remember indicator of what this plugins does, like if it's a
|
||||
package that adds an integration with a system named Carmen, you would want to name it `carmen`.
|
||||
|
||||
This will create a new NPM package with a package name something like `@internal/plugin-carmen-backend`, depending on the other flags passed to the `new` command, and your settings for the `new` command in your root `package.json`. For future reference, we also support additional flags and configuration. Learn more at [the CLI docs](../../../tooling/cli/03-commands.md#new).
|
||||
|
||||
Creating the plugin will take a little while, so be patient. If it runs with no issues, it will run the initial installation and build commands, so that your package is ready to be hacked on!
|
||||
|
||||
Once the commands complete, you should see a new folder `plugins/todo-backend` with content like the below tree:
|
||||
|
||||
```
|
||||
/ <- your Backstage app's root directory
|
||||
/plugins/
|
||||
/todo-backend/
|
||||
package.json
|
||||
README.md
|
||||
eslintrc.js
|
||||
/dev/
|
||||
index.ts
|
||||
/src/
|
||||
plugin.ts
|
||||
index.ts
|
||||
router.ts
|
||||
/services/
|
||||
/TodoListService/
|
||||
TodoListService.ts
|
||||
types.ts
|
||||
index.ts
|
||||
```
|
||||
|
||||
<!-- TODO: describe each of the above files -->
|
||||
|
||||
### FAQs
|
||||
|
||||
<!-- List of commonly occurring problems during install -->
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
id: 002-poking-around
|
||||
sidebar_label: 002 - Poking around
|
||||
title: 002 - Poking around
|
||||
---
|
||||
|
||||
## Default plugin functionality
|
||||
|
||||
By default, that plugin that you just created hosts a simple todo list application. It exposes an HTTP API at `http://localhost:7007/api/todo/todos` that allows you to create TODOs, list existing TODOs, and get a specific TODO. It stores those TODOs in memory, which means that you would lose all of your TODOs if you restarted your application. It also allows you to tag TODOs with a Software Catalog entity, which will be useful for our frontend integration.
|
||||
|
||||
To make this plugin production ready, we'll need to adjust a few things,
|
||||
|
||||
1. Write our TODOs to a database so they don't get lost on restart.
|
||||
2. Write some proper tests to make sure everything works the way we expect.
|
||||
3. Get user feedback.
|
||||
|
||||
## Testing locally
|
||||
|
||||
Before we jump in to making this plugin ready to ship, let's walk through how to run it locally. If you open your backend plugin's manifest (`plugins/todo-backend/package.json`), and look at the `scripts` section, you'll notice a few important commands. The ones relevant to use right now are
|
||||
|
||||
1. `yarn start` - Starts a local development server using the content in `dev/index.ts` as the backend.
|
||||
2. `yarn test` - Runs all of the tests for your backend plugin.
|
||||
|
||||
If you run `yarn start`, you should see a custom backend for just your plugin start up. This will simplify plugin development and iteration for you or your team by easily testing out new features in just your plugin - just make sure you add what you need to the global `packages/backend`. The important log for us to look for is
|
||||
|
||||
```
|
||||
2025-06-08T16:14:53.229Z rootHttpRouter info Listening on :7007
|
||||
```
|
||||
|
||||
This indicates that your HTTP server is up and running and we can start sending test HTTP requests. Grab your favorite HTTP client and let's get testing! If you aren't sure what to use, I'd recommend the `humao.rest-client` VSCode extension which can easily be run in VSCode itself with very little extra set up.
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
-38
@@ -88,44 +88,6 @@ After verifying everything, introduce the problem of persistence - the todos are
|
||||
|
||||
Saving values to the database. Writing a migrations file. Plumbing through the database service.
|
||||
|
||||
## Integrations
|
||||
|
||||
Now that our plugin is ready for prime time, let's see how we can really leverage the rest of the Backstage ecosystem. Backstage provides a set of core features out of the box, namely, the Software Catalog, Search, Permissions, and Notifications.
|
||||
|
||||
### Catalog
|
||||
|
||||
We want to show our todos as separate Catalog entities. How can we make this happen?
|
||||
|
||||
### Search
|
||||
|
||||
We want to make our todos searchable.
|
||||
|
||||
### Permissions
|
||||
|
||||
We only want users to be able to find their own todos.
|
||||
|
||||
### Notifications
|
||||
|
||||
We want to set an alarm time for todos that sends a notification when the time is met.
|
||||
|
||||
## SCM Integrations
|
||||
|
||||
Our users love the new plugin, and now they want it to automatically fetch todos from their source code.
|
||||
|
||||
## Additional Resources and Further Reading
|
||||
|
||||
- **Real-world Implementations and Lessons**
|
||||
|
||||
- [Case studies and examples from the community](https://github.com/backstage/community#newsletters).
|
||||
- Best practices derived from mature implementations.
|
||||
- [Existing open-source community-maintained plugins](https://github.com/backstage/community-plugins).
|
||||
|
||||
- **Resource Compendium**
|
||||
|
||||
- [Backstage Glossary](https://backstage.io/docs/references/glossary) of key terms.
|
||||
- Recommended readings and tools for advanced developers.
|
||||
|
||||
- **Certification and Learning Pathways**
|
||||
- Pathways to deepen your understanding and expertise in plugin development for Backstage.
|
||||
|
||||
Stay tuned for detailed exploration and guidance in each of these modules. We're excited to accompany you on your plugin development journey!
|
||||
@@ -0,0 +1,23 @@
|
||||
## Learning Recap
|
||||
|
||||
With this golden path, you learned how to,
|
||||
|
||||
<!-- TODO -->
|
||||
|
||||
## Additional Resources and Further Reading
|
||||
|
||||
- **Real-world Implementations and Lessons**
|
||||
|
||||
- [Case studies and examples from the community](https://github.com/backstage/community#newsletters).
|
||||
- Best practices derived from mature implementations.
|
||||
- [Existing open-source community-maintained plugins](https://github.com/backstage/community-plugins).
|
||||
|
||||
- **Resource Compendium**
|
||||
|
||||
- [Backstage Glossary](https://backstage.io/docs/references/glossary) of key terms.
|
||||
- Recommended readings and tools for advanced developers.
|
||||
|
||||
- **Certification and Learning Pathways**
|
||||
- Pathways to deepen your understanding and expertise in plugin development for Backstage.
|
||||
|
||||
Stay tuned for detailed exploration and guidance in each of these modules. We're excited to accompany you on your plugin development journey!
|
||||
@@ -0,0 +1,12 @@
|
||||
POST http://localhost:7007/api/todo/todos
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "My First TODO"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
GET http://localhost:7007/api/todo/todos
|
||||
|
||||
###
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
id: index
|
||||
sidebar_label: Backstage Plugins!
|
||||
title: How to create plugins with Backstage
|
||||
---
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- We expect that you have finished the create-app golden path.
|
||||
|
||||
### Scenario
|
||||
|
||||
You have an awesome idea to create a todo list tracker in your Backstage instance at an upcoming company hackathon. Backstage is supposed to unify all of our information after all, it should track future tasks to complete as well!
|
||||
|
||||
Many of the great Backstage plugins started in a similar way, a developer noticed that others on their team or in the company were:
|
||||
|
||||
- Wasting time manually compiling spreadsheets filled with error-prone data
|
||||
- Spending hours every week trying to find that one specific link from that one site
|
||||
- A million other problems that impact developer flow or are just toil
|
||||
|
||||
And they decided to create a shared plugin in Backstage to solve that problem.
|
||||
|
||||
This guide will teach you how to deliver high-quality Backstage plugins with confidence. Both so you can impress everyone at the hackathon and set yourself up for success when you inevitably are asked to make your plugin production-ready.
|
||||
|
||||
### Structure
|
||||
|
||||
To start, this guide will walk through creating a backend plugin. You'll get your feet wet working with an HTTP API, a database and the Backstage backend system. Then, we'll move to the frontend, where we'll show you how to create a new page that's visible to your Backstage users as well as how to call your API. Finally, we'll walk through some common integrations you may want to consider as you write plugins.
|
||||
|
||||
### Next Steps
|
||||
|
||||
- [Why build plugins?](./why-build-plugins.md)
|
||||
- [Sustainable plugin development](./sustainable-plugin-development.md)
|
||||
- [Golden path: Backend plugins](./backend/001-first-steps.md)
|
||||
@@ -0,0 +1,19 @@
|
||||
## Integrations
|
||||
|
||||
Now that our plugin is ready for prime time, let's see how we can really leverage the rest of the Backstage ecosystem. Backstage provides a set of core features out of the box, namely, the Software Catalog, Search, Permissions, and Notifications.
|
||||
|
||||
### Catalog
|
||||
|
||||
We want to show our todos as separate Catalog entities. How can we make this happen?
|
||||
|
||||
### Search
|
||||
|
||||
We want to make our todos searchable.
|
||||
|
||||
### Permissions
|
||||
|
||||
We only want users to be able to find their own todos.
|
||||
|
||||
### Notifications
|
||||
|
||||
We want to set an alarm time for todos that sends a notification when the time is met.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
id: sustainable-plugin-development
|
||||
sidebar_label: Sustainable plugin development
|
||||
title: Sustainably developing plugins in Backstage
|
||||
---
|
||||
|
||||
Plugins are not created in a vacuum, they generally solve a customer ask, be that
|
||||
|
||||
- a business problem, like showing cloud spend
|
||||
- a new integration, like showing data from an external vendor such as Pagerduty
|
||||
- a developer pain point, like organizing information from disjoint or disorganized systems.
|
||||
|
||||
To ensure that your plugin lives the test of time, you'll need to figure out how to keep it up-to-date,
|
||||
|
||||
### Finding your stakeholders
|
||||
|
||||
<!-- TODO -->
|
||||
|
||||
### Iterating on your plugin
|
||||
|
||||
In many cases, your first version of a plugin will cut a few corners - this is a good sign, you're more focused on delivering a strong use case to continue development than over-indexing on your initial code. It may be temporary after all, if you don't get the response you're looking for!
|
||||
|
||||
So, how do you decide when you should iterate on your plugin?
|
||||
|
||||
<!-- TODO -->
|
||||
|
||||
### Ensuring the success of your plugin
|
||||
|
||||
<!-- TODO -->
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
id: why-build-plugins
|
||||
sidebar_label: Why build plugins?
|
||||
title: Introduction to the Value and Impact of Plugins within Backstage
|
||||
---
|
||||
|
||||
Backstage plugins are essential components that enable the integration of various tools and services into a unified developer portal. Here’s a detailed look at why building plugins for Backstage can be highly beneficial:
|
||||
|
||||
## Enhancing Developer Productivity
|
||||
|
||||
Plugins in Backstage centralize and simplify access to tools, reducing the time developers spend switching between different systems. By providing a consistent interface and user experience, plugins minimize cognitive load and streamline workflows, allowing developers to focus more on coding and less on tool management. Plugins can leverage platform APIs and reusable UI components to integrate external data and services seamlessly.
|
||||
|
||||
## Customizable and Extensible Platform
|
||||
|
||||
Backstage's plugin architecture is designed to be highly flexible, enabling the integration of nearly any infrastructure or software development tool. This extensibility allows organizations to tailor Backstage to meet their specific needs, integrating internal tools, third-party services, and other custom functionalities seamlessly. For detailed guidelines on plugin development, refer to the [Backstage Plugin Development documentation](https://backstage.io/docs/plugins/plugin-development/).
|
||||
|
||||
## Improved Collaboration and Knowledge Sharing
|
||||
|
||||
Plugins facilitate better collaboration among teams by consolidating documentation, code repositories, CI/CD pipelines, and monitoring tools in one place. This centralization makes it easier for team members to find information, share insights, and collaborate on projects, enhancing overall team productivity and coherence.
|
||||
|
||||
## Consistency and Best Practices
|
||||
|
||||
By adhering to Backstage’s design guidelines, plugins ensure a consistent user experience across the platform. This consistency helps in maintaining usability and reducing the learning curve for new users, promoting the adoption of best practices within development teams. More details can be found in the [Introduction to Plugins](https://backstage.io/docs/plugins/) section.
|
||||
|
||||
## Scalability and Maintenance
|
||||
|
||||
Backstage plugins are designed to be modular and independent, allowing for easy updates and maintenance without affecting the overall system. This modularity supports horizontal scalability, where each plugin can be scaled independently according to the needs of the application, ensuring robust performance and reliability. For more on structuring and connecting plugins, see the [Structure of a Plugin](https://backstage.io/docs/plugins/structure-of-a-plugin) documentation.
|
||||
|
||||
## Community and Ecosystem Growth
|
||||
|
||||
Developing and contributing plugins to the Backstage community helps in expanding the ecosystem. Open-source contributions foster innovation and collaboration, allowing developers to leverage a wide range of existing plugins and avoid reinventing the wheel. This collaborative environment accelerates the development of new features and enhances the overall value of the Backstage platform.
|
||||
|
||||
Building plugins for Backstage significantly enhances developer productivity, promotes best practices, supports scalability, and fosters community growth. These plugins transform Backstage into a comprehensive developer portal that can adapt to the unique needs of any organization. For more detailed information, refer to the [Backstage Plugin Development documentation](https://backstage.io/docs/plugins/plugin-development/) and the [Introduction to Plugins](https://backstage.io/docs/plugins/).
|
||||
@@ -58,7 +58,7 @@ To receive the `repository.transferred` event, the new owner account must have t
|
||||
|
||||
:::
|
||||
|
||||
When creating the webhook in GitHub the "Payload URL" will looks something along these lines: `https://<your-intance-name>/api/events/http/github` and the "Content Type" should be `application/json`.
|
||||
When creating the webhook in GitHub the "Payload URL" will looks something along these lines: `https://<your-instance-name>/api/events/http/github` and the "Content Type" should be `application/json`.
|
||||
|
||||
The GitHub Webhooks UI will send a trial event to validate it can connect when you save your new Webhook. It is possible to retry this trial event if it fails and you want to send it again. Additionally there is a Recent Deliveries tab you can use to validate that the events are being fired should you need to do any later troubleshooting.
|
||||
|
||||
@@ -210,6 +210,8 @@ catalog:
|
||||
filters: # optional filters
|
||||
branch: 'develop' # optional string
|
||||
repository: '.*' # optional Regex
|
||||
pageSizes:
|
||||
repositories: 25
|
||||
wildcardProviderId:
|
||||
organization: 'new-org' # string
|
||||
catalogPath: '/groups/**/*.yaml' # this will search all folders for files that end in .yaml
|
||||
@@ -308,6 +310,10 @@ If you do so, `default` will be used as provider ID.
|
||||
The amount of time that should pass before the first invocation happens.
|
||||
- **`scope`** _(optional)_:
|
||||
`'global'` or `'local'`. Sets the scope of concurrency control.
|
||||
- **`pageSizes`** _(optional)_:
|
||||
Configure page sizes for GitHub GraphQL API queries. This can help prevent `RESOURCE_LIMITS_EXCEEDED` errors.
|
||||
- **`repositories`** _(optional)_:
|
||||
Number of repositories to fetch per page. Defaults to `25`. Reduce this value if hitting API resource limits.
|
||||
|
||||
## GitHub API Rate Limits
|
||||
|
||||
|
||||
@@ -79,6 +79,10 @@ catalog:
|
||||
initialDelay: { seconds: 30 }
|
||||
frequency: { hours: 1 }
|
||||
timeout: { minutes: 50 }
|
||||
pageSizes:
|
||||
teams: 25
|
||||
teamMembers: 50
|
||||
organizationMembers: 50
|
||||
- id: ghe
|
||||
githubUrl: https://ghe.mycompany.com
|
||||
orgs: ['internal-1', 'internal-2', 'internal-3']
|
||||
@@ -94,6 +98,13 @@ Directly under the `githubOrg` is a list of configurations, each entry is a stru
|
||||
- `githubUrl`: The target that this provider should consume
|
||||
- `orgs` (optional): The list of the GitHub orgs to consume. If you only list a single org the generated group entities will use the `default` namespace, otherwise they will use the org name as the namespace. By default the provider will consume all accessible orgs on the given GitHub instance (support for GitHub App integration only).
|
||||
- `schedule`: The refresh schedule to use, matches the structure of [`SchedulerServiceTaskScheduleDefinitionConfig`](https://backstage.io/docs/reference/backend-plugin-api.schedulerservicetaskscheduledefinitionconfig/)
|
||||
- `pageSizes` (optional): Configure page sizes for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors. You can configure the following page sizes:
|
||||
|
||||
- `teams`: Number of teams to fetch per page when querying organization teams (default: 25)
|
||||
- `teamMembers`: Number of team members to fetch per page when querying team members (default: 50)
|
||||
- `organizationMembers`: Number of organization members to fetch per page (default: 50)
|
||||
|
||||
Reducing page sizes will result in more API calls and slightly longer sync times, but will prevent API resource limits for organizations with large number of teams and members.
|
||||
|
||||
### Events Support
|
||||
|
||||
|
||||
@@ -164,6 +164,61 @@ You can customize the origin names shown in the UI by passing an object where th
|
||||
|
||||
Each notification processor will receive its own row in the settings page, where the user can enable or disable notifications from that processor.
|
||||
|
||||
### Default notification settings
|
||||
|
||||
You can configure default notification settings for all users in your `app-config.yaml` file. This allows you to set up notification preferences globally, such as disabling specific channels or origins by default, implementing an opt-in strategy instead of opt-out.
|
||||
|
||||
#### Channel-level defaults
|
||||
|
||||
You can set a default enabled state for an entire channel. When set to `false`, the channel uses an opt-in strategy where notifications are disabled by default unless explicitly enabled by the user or for specific origins.
|
||||
|
||||
```yaml
|
||||
notifications:
|
||||
defaultSettings:
|
||||
channels:
|
||||
- id: 'Web'
|
||||
enabled: false # Opt-in strategy: channel disabled by default
|
||||
- id: 'Email'
|
||||
enabled: true # Opt-out strategy: channel enabled by default (default behavior)
|
||||
```
|
||||
|
||||
#### Origin-level defaults
|
||||
|
||||
You can also configure defaults for specific origins within a channel:
|
||||
|
||||
```yaml
|
||||
notifications:
|
||||
defaultSettings:
|
||||
channels:
|
||||
- id: 'Web'
|
||||
enabled: true # Channel is enabled by default
|
||||
origins:
|
||||
- id: 'plugin:scaffolder'
|
||||
enabled: false # Disable scaffolder notifications by default
|
||||
- id: 'plugin:catalog'
|
||||
enabled: true # Enable catalog notifications by default
|
||||
```
|
||||
|
||||
#### Topic-level defaults
|
||||
|
||||
For even more granular control, you can set defaults for specific topics within origins:
|
||||
|
||||
```yaml
|
||||
notifications:
|
||||
defaultSettings:
|
||||
channels:
|
||||
- id: 'Email'
|
||||
enabled: false # Email is opt-in by default
|
||||
origins:
|
||||
- id: 'plugin:catalog'
|
||||
enabled: true # But catalog notifications are enabled
|
||||
topics:
|
||||
- id: 'entity:validation:error'
|
||||
enabled: false # Except validation errors
|
||||
```
|
||||
|
||||
**Note:** If a channel's `enabled` flag is not set, it defaults to `true` for backwards compatibility. When a channel is set to `enabled: false`, all origins within that channel default to disabled unless explicitly enabled.
|
||||
|
||||
### Automatic notification cleanup
|
||||
|
||||
Notifications are deleted automatically after a certain period of time to prevent the database from growing indefinitely
|
||||
|
||||
@@ -48,11 +48,20 @@ This will think for a bit, and then say `Listening on :7007`. In a different
|
||||
terminal window, now run
|
||||
|
||||
```sh
|
||||
curl localhost:7007/api/carmen/health
|
||||
curl localhost:7007/api/carmen/todos
|
||||
```
|
||||
|
||||
This should return `{"status":"ok"}`. Success! Press `Ctrl + c` to stop it
|
||||
again.
|
||||
You should see the following response:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": []
|
||||
}
|
||||
```
|
||||
|
||||
:::note Note: The route shown here matches the default in the current backend plugin template. If you want a `/health` endpoint for health checks, you can add it to your router yourself.
|
||||
|
||||
:::
|
||||
|
||||
## Developing your Backend Plugin
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,282 @@
|
||||
# Release v1.45.0-next.3
|
||||
|
||||
Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.45.0-next.3](https://backstage.github.io/upgrade-helper/?to=1.45.0-next.3)
|
||||
|
||||
## @backstage/backend-plugin-api@1.5.0-next.2
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 62fc2de: Explicitly mark `coreServices.rootInstanceMetadata` as a root service.
|
||||
|
||||
## @backstage/repo-tools@0.16.0-next.2
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 11c61f2: The `package-docs` command will now automatically use a `typedoc.json` file if one exists at the root of your project.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
|
||||
## @backstage/ui@0.9.0-next.3
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 83c100e: **BREAKING**: Removed `Collapsible` component. Migrate to `Accordion` or use React Aria `Disclosure`.
|
||||
|
||||
## Migration Path 1: Accordion (Opinionated Styled Component)
|
||||
|
||||
Accordion provides preset styling with a similar component structure.
|
||||
|
||||
```diff
|
||||
- import { Collapsible } from '@backstage/ui';
|
||||
+ import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui';
|
||||
|
||||
- <Collapsible.Root>
|
||||
- <Collapsible.Trigger render={(props) => <Button {...props}>Toggle</Button>} />
|
||||
- <Collapsible.Panel>Content</Collapsible.Panel>
|
||||
- </Collapsible.Root>
|
||||
|
||||
+ <Accordion>
|
||||
+ <AccordionTrigger title="Toggle" />
|
||||
+ <AccordionPanel>Content</AccordionPanel>
|
||||
+ </Accordion>
|
||||
```
|
||||
|
||||
CSS classes: `.bui-CollapsibleRoot` → `.bui-Accordion`, `.bui-CollapsibleTrigger` → `.bui-AccordionTrigger` (now on heading element), `.bui-CollapsiblePanel` → `.bui-AccordionPanel`
|
||||
|
||||
## Migration Path 2: React Aria Disclosure (Full Customization)
|
||||
|
||||
For custom styling without preset styles:
|
||||
|
||||
```tsx
|
||||
import { Disclosure, Button, DisclosurePanel } from 'react-aria-components';
|
||||
|
||||
<Disclosure>
|
||||
<Button slot="trigger">Toggle</Button>
|
||||
<DisclosurePanel>Content</DisclosurePanel>
|
||||
</Disclosure>;
|
||||
```
|
||||
|
||||
- 816af0f: **BREAKING**: The `SelectProps` interface now accepts a generic type parameter for selection mode.
|
||||
|
||||
Added searchable and multiple selection support to Select component. The component now accepts `searchable`, `selectionMode`, and `searchPlaceholder` props to enable filtering and multi-selection modes.
|
||||
|
||||
Migration: If you're using `SelectProps` type directly, update from `SelectProps` to `SelectProps<'single' | 'multiple'>`. Component usage remains backward compatible.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 35a3614: Fixed CSS issues in Select component including popover width constraints, focus outline behavior, and overflow handling.
|
||||
- 01476f0: Improved visual consistency of PasswordField, SearchField, and MenuAutocomplete components.
|
||||
- 836b0c7: Fixed dialog backdrop appearance in dark mode.
|
||||
- 6d35a6b: Removed `@base-ui-components/react` dependency as all components now use React Aria Components.
|
||||
- 7839e7b: Added `loading` prop to Button and ButtonIcon components for displaying spinner during async operations.
|
||||
- a00fb88: Fixed Table Row component to properly support opening links in new tabs via right-click or Cmd+Click when using the href prop.
|
||||
|
||||
## @backstage/plugin-notifications-backend@0.6.0-next.2
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 87e597c: Adds support for default configuration for an entire notification channel.
|
||||
This setting will also be inherited down to origins and topics while still respecting the users individual choices.
|
||||
|
||||
This will be handy if you want to use a "opt-in" strategy.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-notifications-common@0.2.0-next.1
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
- @backstage/plugin-notifications-node@0.2.21-next.2
|
||||
|
||||
## @backstage/plugin-notifications-common@0.2.0-next.1
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 87e597c: Adds support for default configuration for an entire notification channel.
|
||||
This setting will also be inherited down to origins and topics while still respecting the users individual choices.
|
||||
|
||||
This will be handy if you want to use a "opt-in" strategy.
|
||||
|
||||
## @backstage/backend-test-utils@1.10.0-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f3001fd: Tweak some of the mock services to have more precise types
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
|
||||
## @backstage/core-components@0.18.3-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 96ad674: Line numbers in LogViewer will not be selectable in UI anymore
|
||||
|
||||
## @backstage/create-app@0.7.6-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Bumped create-app version.
|
||||
|
||||
## @backstage/frontend-plugin-api@0.12.2-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 7c6a66d: Added support for plugin-relative `attachTo` declarations for extension definitions. This allows for the creation of extension and extension blueprints that attach to other extensions of a particular `kind` in the same plugin, rather than needing to provide the exact extension ID. This is particularly useful when wanting to provide extension blueprints with a built-in hierarchy where the extensions created from one blueprint attach to extensions created from the other blueprint, for example:
|
||||
|
||||
```ts
|
||||
// kind: 'tabbed-page'
|
||||
const parentPage = TabbedPageBlueprint.make({
|
||||
params: {....}
|
||||
})
|
||||
// attachTo: { kind: 'tabbed-page', input: 'tabs' }
|
||||
const child1 = TabContentBlueprint.make({
|
||||
name: 'tab1',
|
||||
params: {....}
|
||||
})
|
||||
```
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-app-visualizer@0.1.25-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 722e2df: Migrated to use `@backstage/ui`.
|
||||
- Updated dependencies
|
||||
- @backstage/ui@0.9.0-next.3
|
||||
- @backstage/frontend-plugin-api@0.12.2-next.2
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-catalog-graph@0.5.3-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a2d7ae7: Ensure the catalog graph entity card respects the height prop so the visualization scales down properly on wide screens.
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog-react@1.21.3-next.2
|
||||
- @backstage/frontend-plugin-api@0.12.2-next.2
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-catalog-react@1.21.3-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 36d7582: Added missing i18n
|
||||
- Updated dependencies
|
||||
- @backstage/frontend-plugin-api@0.12.2-next.2
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-home@0.8.14-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2ac5d29: Allow customization of VisitList by adding optional enrichVisit, transformPathname, canSave functions to VisitsStorageApi, along with VisitDisplayProvider for colors, labels
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog-react@1.21.3-next.2
|
||||
- @backstage/frontend-plugin-api@0.12.2-next.2
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-kubernetes-backend@0.20.4-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
- @backstage/plugin-kubernetes-common@0.9.8-next.1
|
||||
- @backstage/plugin-kubernetes-node@0.3.6-next.2
|
||||
|
||||
## @backstage/plugin-kubernetes-common@0.9.8-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`.
|
||||
|
||||
## @backstage/plugin-kubernetes-node@0.3.6-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
- @backstage/plugin-kubernetes-common@0.9.8-next.1
|
||||
|
||||
## @backstage/plugin-kubernetes-react@0.5.13-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-kubernetes-common@0.9.8-next.1
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-notifications@0.5.11-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-notifications-common@0.2.0-next.1
|
||||
- @backstage/frontend-plugin-api@0.12.2-next.2
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-notifications-backend-module-email@0.3.16-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-notifications-common@0.2.0-next.1
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
- @backstage/plugin-notifications-node@0.2.21-next.2
|
||||
|
||||
## @backstage/plugin-notifications-backend-module-slack@0.2.1-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-notifications-common@0.2.0-next.1
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
- @backstage/plugin-notifications-node@0.2.21-next.2
|
||||
|
||||
## @backstage/plugin-notifications-node@0.2.21-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-notifications-common@0.2.0-next.1
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
|
||||
## @backstage/plugin-org@0.6.46-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 6db9e7e: Improved responsiveness of GroupProfileCard component
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog-react@1.21.3-next.2
|
||||
- @backstage/frontend-plugin-api@0.12.2-next.2
|
||||
- @backstage/core-components@0.18.3-next.2
|
||||
|
||||
## @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-notifications-common@0.2.0-next.1
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
- @backstage/plugin-notifications-node@0.2.21-next.2
|
||||
|
||||
## example-backend@0.0.44-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-notifications-backend@0.6.0-next.2
|
||||
- @backstage/backend-plugin-api@1.5.0-next.2
|
||||
- @backstage/plugin-kubernetes-backend@0.20.4-next.2
|
||||
- @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.2
|
||||
- @backstage/plugin-catalog-backend@3.2.0-next.1
|
||||
- @backstage/plugin-events-backend@0.5.8-next.1
|
||||
- @backstage/plugin-scaffolder-backend@3.0.1-next.1
|
||||
- @backstage/plugin-search-backend@2.0.8-next.1
|
||||
Reference in New Issue
Block a user