Merge branch 'backstage:master' into feature/catalog-export

This commit is contained in:
1337
2026-05-20 20:32:27 +02:00
committed by GitHub
1172 changed files with 67648 additions and 10161 deletions
@@ -52,16 +52,15 @@ _Example disabling the search page extension_
app:
extensions:
- page:search: false # ✨
- nav-item:search: false # ✨
```
_Example setting the search sidebar item title_
_Example setting the search page title (used in the sidebar)_
```yaml
# app-config.yaml
app:
extensions:
- nav-item:search: # ✨
- page:search: # ✨
config:
title: 'Search Page'
```
@@ -109,6 +109,10 @@ Default secrets are resolved from environment variables and accessible via `${{
## Customizing the ScaffolderPage with Grouping and Filtering
The sections below cover the legacy (JSX) frontend system. For the new
frontend system, see [Customizing the templates page in the new frontend system](#customizing-the-templates-page-in-the-new-frontend-system)
below.
Once you have more than a few software templates you may want to customize your
`ScaffolderPage` by grouping and surfacing certain templates together. You can
accomplish this by creating `groups` and passing them to your `ScaffolderPage`
@@ -149,3 +153,74 @@ You can have several use cases for that:
}
/>
```
## Customizing the templates page in the new frontend system
In the new frontend system the templates page is built from extensions, so
customisations are configured rather than passed as JSX props.
### Defining template groups in `app-config.yaml`
The `sub-page:scaffolder/templates` extension accepts a `groups` config field.
Each group has a `title` and a `filter` predicate (using
[entity predicate queries](https://backstage.io/docs/features/software-catalog/catalog-customization#entity-predicate-queries)).
Templates not matched by any group fall into an automatically appended
"Other Templates" group. With no groups configured the page renders a single
"Templates" group.
```yaml
app:
extensions:
- sub-page:scaffolder/templates:
config:
groups:
- title: Recommended Services
filter:
spec.type: service
- title: Documentation
filter:
spec.type: documentation
```
Predicate values are matched case-insensitively. The matchers `$exists`,
`$in`, `$contains`, `$hasPrefix` and the logical operators `$all`, `$any`, `$not`
are also supported — see the
[entity predicate queries reference](https://backstage.io/docs/features/software-catalog/catalog-customization#entity-predicate-queries)
for the full grammar.
### Replacing the default `TemplateCard`
The `TemplateCard` exported from `@backstage/plugin-scaffolder-react/alpha`
is a swappable component. Apps can replace it by registering a
`SwappableComponentBlueprint` extension that targets `TemplateCard`:
```tsx
// packages/app/src/modules/appModuleScaffolder.tsx
import { createFrontendModule } from '@backstage/frontend-plugin-api';
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
import { TemplateCard } from '@backstage/plugin-scaffolder-react/alpha';
export const appModuleScaffolder = createFrontendModule({
pluginId: 'app',
extensions: [
SwappableComponentBlueprint.make({
name: 'scaffolder-template-card',
params: defineParams =>
defineParams({
component: TemplateCard,
loader: () => import('./MyTemplateCard').then(m => m.MyTemplateCard),
}),
}),
],
});
```
Wire the module into your app by adding `appModuleScaffolder` to the
`features` array of `createApp` in `packages/app/src/App.tsx`.
`MyTemplateCard` receives `TemplateCardComponentProps`
(`{ template, additionalLinks?, onSelected? }`). The list takes care of
binding the template to `onSelected`, so the card just calls
`props.onSelected?.()` to choose itself. The example app under
`packages/app/src/modules/BuiTemplateCard.tsx` shows a Backstage UI (BUI)
implementation you can use as a starting point.
@@ -73,34 +73,20 @@ You don't need to provide any extra configuration, but you have to be sure that
## Form Decorators
Form decorators provide the ability to run arbitrary code before the form is submitted along with secrets to the `scaffolder-backend` plugin. They are provided to the `app` using a Utility API.
Form decorators provide the ability to run arbitrary code before the form is submitted along with secrets to the `scaffolder-backend` plugin.
#### Installation
#### Configuring templates
To install the Form Decorators, add the following to your `packages/app/src/apis.ts`:
```ts
createApiFactory({
api: formDecoratorsApiRef,
deps: {},
factory: () =>
DefaultScaffolderFormDecoratorsApi.create({
decorators: [
// add decorators here
],
}),
}),
```
And then you'll also need to define which decorators run in each template using the `EXPERIMENTAL_formDecorators` key in the template's `spec`:
Define which decorators run in each template using the `formDecorators` key in the template's `spec`:
```yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: my-template
spec:
EXPERIMENTAL_formDecorators:
- id: myDecorator
formDecorators:
- id: mockDecorator
input:
test: something funky
@@ -108,36 +94,67 @@ spec:
steps: ...
```
#### Creating a Decorator
:::note
The legacy `EXPERIMENTAL_formDecorators` field is still supported but deprecated. Migrate to `formDecorators` when possible.
:::
You can create a decorator using the simple helper method `createScaffolderFormDecorator`:
#### Creating a decorator
Create a decorator with `createScaffolderFormDecorator` and register it as an extension using `FormDecoratorBlueprint`:
```ts
export const mockDecorator = createScaffolderFormDecorator({
// give the decorator a name
id: 'mockDecorator',
import { createScaffolderFormDecorator } from '@backstage/plugin-scaffolder-react/alpha';
import { githubAuthApiRef } from '@backstage/core-plugin-api';
// define the schema for the input that can be provided in `template.yaml`
const mockDecorator = createScaffolderFormDecorator({
id: 'mockDecorator',
schema: {
input: {
test: z => z.string(),
},
},
deps: {
// define dependencies here
githubApi: githubAuthApiRef,
},
decorator: async (
// Context has all the things needed to write simple decorators
{ setSecrets, setFormState, input: { test } },
// Depepdencies injected here
{ githubApi },
) => {
// mutate the form state
setFormState(state => ({ ...state, test, mock: 'MOCK' }));
// mutate the form secrets
setSecrets(state => ({ ...state, GITHUB_TOKEN: 'MOCK_TOKEN' }));
const token = await githubApi.getAccessToken(['repo']);
setFormState(state => ({ ...state, test }));
setSecrets(state => ({ ...state, GITHUB_TOKEN: token }));
},
});
```
#### Installation (new frontend system)
Register your decorator as an extension using `FormDecoratorBlueprint`:
```ts
import { FormDecoratorBlueprint } from '@backstage/plugin-scaffolder-react/alpha';
export const myDecoratorExtension = FormDecoratorBlueprint.make({
name: 'my-decorator',
params: {
decorator: mockDecorator,
},
});
```
Then install the extension in your app or plugin.
#### Installation (legacy frontend system)
For apps using the legacy frontend system, provide decorators through a Utility API in `packages/app/src/apis.ts`:
```ts
createApiFactory({
api: formDecoratorsApiRef,
deps: {},
factory: () =>
DefaultScaffolderFormDecoratorsApi.create({
decorators: [mockDecorator],
}),
}),
```
@@ -746,6 +746,75 @@ input:
When `each` is used, the outputs of a repeated step are returned as an array of outputs from each iteration.
### Status Check Functions - `always()` and `failure()`
By default, when a step fails during a scaffolder run, all subsequent steps are skipped and the task is marked as failed. This can be problematic when your template creates external resources (repositories, cloud infrastructure, deployments) that need to be cleaned up if a later step fails.
Status check functions give you control over which steps run even after a failure. You use them inside a `${{ ... }}` template expression in the `if` field of a step.
| Function | Description |
| ----------- | ---------------------------------------------------------------------------- |
| `always()` | Always runs the step, regardless of whether previous steps passed or failed. |
| `failure()` | Runs the step only when a previous step has failed. |
These functions must be used as template expressions such as `${{ always() }}` or `${{ failure() }}`.
After a step has failed, the scaffolder only attempts later steps whose `if` expression invokes one of these status check functions.
#### Usage
```yaml
steps:
- id: cleanup
name: Cleanup Resources
action: my:cleanup:action
if: ${{ always() }}
```
#### Example: Cleanup on failure
A common pattern is to create resources in early steps and add cleanup steps
that only run if something goes wrong:
```yaml
steps:
- id: create-repo
name: Create Repository
action: publish:github
input:
repoUrl: ${{ parameters.repoUrl }}
- id: deploy
name: Deploy to Kubernetes
action: deploy:kubernetes
input:
manifest: ./k8s/deployment.yaml
# Only runs when a previous step failed — cleans up the repository
- id: cleanup-repo
name: Delete Repository
action: github:repo:delete
if: ${{ failure() }}
input:
repoUrl: ${{ parameters.repoUrl }}
# Always runs — post an audit event regardless of outcome
- id: audit
name: Post Audit Event
action: debug:log
if: ${{ always() }}
input:
message: 'Scaffolder run completed for ${{ parameters.repoUrl }}'
# Does not run after a failure, because it does not invoke a status check function
- id: plain-truthy-condition
name: Plain Truthy Condition
action: debug:log
if: ${{ true }}
input:
message: 'This step is skipped after a previous failure'
```
## Outputs
Each individual step can output some variables that can be used in the
+1 -1
View File
@@ -31,7 +31,7 @@ The static files consist of HTML, CSS and Images generated by MkDocs. We remove
all the JavaScript before adding them to Backstage for security reasons. And
there is an additional `techdocs_metadata.json` file that TechDocs needs to
render a site. It's important that you use either
[techdocs-cli](https://github.com/backstage/techdocs-cli) or
[techdocs-cli](https://github.com/backstage/backstage/tree/master/packages/techdocs-cli) or
[techdocs-container](https://github.com/backstage/techdocs-container) to
generate the docs for the expected output.
+2
View File
@@ -149,6 +149,8 @@ Options:
Defaults to false, which means that the techdocs-core plugin is always added to the mkdocs file.
--legacyCopyReadmeMdToIndexMd Attempt to ensure an index.md exists falling back to using <docs-dir>/README.md or README.md
in case a default <docs-dir>/index.md is not provided. (default: false)
--disableExternalFonts Disable external font downloads for all TechDocs sites. Useful for air-gapped environments
where Google fonts cannot be accessed. (default: false)
--runAsDefaultUser Bypass setting the container user as the same user and group id as host for Linux and MacOS (default: false)
-v, --verbose Enable verbose output. (default: false)
-h, --help display help for command
+1 -1
View File
@@ -87,7 +87,7 @@ documentation for publishing. Currently it mostly acts as a wrapper around the
TechDocs container and provides an easy-to-use interface for our docker
container.
[TechDocs CLI](https://github.com/backstage/techdocs-cli)
[TechDocs CLI](https://github.com/backstage/backstage/tree/master/packages/techdocs-cli)
## TechDocs Reader
+27
View File
@@ -97,6 +97,33 @@ techdocs:
legacyCopyReadmeMdToIndexMd: false
```
#### Disable external fonts
`techdocs.generator.mkdocs.disableExternalFonts`
(Optional) Use this when the generator cannot reach the internet (for example air-gapped or restricted networks). MkDocs Material otherwise tries to download the Roboto font from Google during generation.
When `true`, TechDocs patches each `mkdocs.yml` during generation: if no `theme` section exists it adds `name: material` and `font: false`; if a `theme` exists but `font` is omitted, it sets `font: false`; if `font` is already set in the file, your value is left unchanged.
**Example:**
```yaml
techdocs:
generator:
mkdocs:
disableExternalFonts: true
```
Alternatively, configure `mkdocs.yml` manually:
```yaml
theme:
name: material
font: false
```
**Note:** When using `theme.font` in `mkdocs.yml`, `theme.name: material` is required. If `font` is already set in the file, app-config patching does not override it; it only adds `font: false` when `font` was not configured.
#### Default Plugins
`techdocs.generator.mkdocs.defaultPlugins`
+1 -1
View File
@@ -92,7 +92,7 @@ the source code hosting provider. Notice that instead of the `dir:` prefix, the
`url:` prefix is used instead. For example:
- **GitHub**: `url:https://githubhost.com/org/repo/tree/<branch_name>`
- **GitLab**: `url:https://gitlabhost.com/org/repo/tree/<branch_name>`
- **GitLab**: `url:https://gitlabhost.com/org/repo`
- **Bitbucket**: `url:https://bitbuckethost.com/project/repo/src/<branch_name>`
- **Azure**: `url:https://azurehost.com/organization/project/_git/repository`
+30 -7
View File
@@ -93,7 +93,7 @@ the source code hosting provider. Notice that instead of the `dir:` prefix, the
`url:` prefix is used instead. For example:
- **GitHub**: `url:https://githubhost.com/org/repo/tree/<branch_name>`
- **GitLab**: `url:https://gitlabhost.com/org/repo/tree/<branch_name>`
- **GitLab**: `url:https://gitlabhost.com/org/repo`
- **Bitbucket**: `url:https://bitbuckethost.com/project/repo/src/<branch_name>`
- **Azure**: `url:https://azurehost.com/organization/project/_git/repository`
@@ -373,9 +373,24 @@ on how you have configured your `template.yaml`.
Done! You now have support for TechDocs in your own software template!
### Prevent download of Google fonts
### Disable external fonts
If your Backstage instance does not have internet access, the generation will fail. TechDocs tries to download the Roboto font from Google. You can disable it by adding the following lines to mkdocs.yaml:
`techdocs.generator.mkdocs.disableExternalFonts`
(Optional) Use this when the generator cannot reach the internet (for example air-gapped or restricted networks). MkDocs Material otherwise tries to download the Roboto font from Google during generation.
When `true`, TechDocs patches each `mkdocs.yml` during generation: if no `theme` section exists it adds `name: material` and `font: false`; if a `theme` exists but `font` is omitted, it sets `font: false`; if `font` is already set in the file, your value is left unchanged.
**Example:**
```yaml
techdocs:
generator:
mkdocs:
disableExternalFonts: true
```
Alternatively, configure `mkdocs.yml` manually:
```yaml
theme:
@@ -383,11 +398,19 @@ theme:
font: false
```
:::note Note
**Note:** When using `theme.font` in `mkdocs.yml`, `theme.name: material` is required. If `font` is already set in the file, app-config patching does not override it; it only adds `font: false` when `font` was not configured.
The addition `name: material` is necessary. Otherwise it will not work
#### Using techdocs-cli in CI/CD
:::
When generating TechDocs sites in CI/CD workflows using `techdocs-cli`, you can
use the `--disableExternalFonts` flag:
```bash
techdocs-cli generate --disableExternalFonts
```
This will automatically patch the `mkdocs.yml` file during the generation
process, just like the `app-config.yaml` option does for local generation.
## How to enable iframes in TechDocs
@@ -805,7 +828,7 @@ metadata:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: example-platfrom
name: example-platform
title: Example Application Platform
namespace: default
description: This is the child entity
@@ -207,7 +207,7 @@ If you need to migrate documentation objects from an older-style path
format including case-sensitive entity metadata, you will need to add some
additional permissions to be able to perform the migration, including:
- `s3:PutBucketAcl` (for copying files,
- `s3:PutObjectAcl` (for copying files,
[more info here](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectAcl.html))
- `s3:DeleteObject` and `s3:DeleteObjectVersion` (for deleting migrated files,
[more info here](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html))