Merge pull request #33069 from awanlin/docs/scaffolder-clean-up

[Doc] Scaffolder clean up
This commit is contained in:
Andre Wanlin
2026-03-17 13:37:57 -05:00
committed by GitHub
9 changed files with 2 additions and 483 deletions
@@ -59,57 +59,3 @@ backend.start();
A list of all registered actions can be found under `/create/actions`. For local
development you should be able to reach them at
`http://localhost:3000/create/actions`.
## Migrating from `fetch:cookiecutter` to `fetch:template`
The `fetch:template` action is a new action with a similar API to
`fetch:cookiecutter` but no dependency on `cookiecutter`. There are two options
for migrating templates that use `fetch:cookiecutter` to use `fetch:template`:
### Using `cookiecutterCompat` mode
The new `fetch:template` action has a `cookiecutterCompat` flag which should
allow most templates built for `fetch:cookiecutter` to work without any changes.
1. Update action name in `template.yaml`. The name should be changed from
`fetch:cookiecutter` to `fetch:template`.
2. Set `cookiecutterCompat` to `true` in the `fetch:template` step input in
`template.yaml`.
```yaml title="template.yaml"
steps:
- id: fetchBase
name: Fetch Base
# highlight-remove-next-line
action: fetch:cookiecutter
# highlight-add-next-line
action: fetch:template
input:
url: ./skeleton
# highlight-add-next-line
cookiecutterCompat: true
values:
```
### Manual migration
If you prefer, you can manually migrate your templates to avoid the need for
enabling cookiecutter compatibility mode, which will result in slightly less
verbose template variables expressions.
1. Update action name in `template.yaml`. The name should be changed from
`fetch:cookiecutter` to `fetch:template`.
2. Update variable syntax in file names and content. `fetch:cookiecutter`
expects variables to be enclosed in `{{` `}}` and prefixed with
`cookiecutter.`, while `fetch:template` expects variables to be enclosed in
`${{` `}}` and prefixed with `values.`. For example, a reference to variable
`myInputVariable` would need to be migrated from
`{{ cookiecutter.myInputVariable }}` to `${{ values.myInputVariable }}`.
3. Replace uses of `jsonify` with `dump`. The
[`jsonify` filter](https://cookiecutter.readthedocs.io/en/latest/advanced/template_extensions.html#jsonify-extension)
is built in to `cookiecutter`, and is not available by default when using
`fetch:template`. The
[`dump` filter](https://mozilla.github.io/nunjucks/templating.html#dump) is
the equivalent filter in nunjucks, so an expression like
`{{ cookiecutter.myAwesomeList | jsonify }}` should be migrated to
`${{ values.myAwesomeList | dump }}`.
@@ -107,29 +107,6 @@ Default secrets are resolved from environment variables and accessible via `${{
**Security Note:** Secrets are automatically masked in logs and are only available to backend actions, never exposed to the frontend.
## Disabling Docker in Docker situation (Optional)
Software templates use the `fetch:template` action by default, which requires no
external dependencies and offers a
[Cookiecutter-compatible mode](https://backstage.io/docs/features/software-templates/builtin-actions#using-cookiecuttercompat-mode).
There is also a `fetch:cookiecutter` action, which uses
[Cookiecutter](https://github.com/cookiecutter/cookiecutter) directly for
templating. By default, the `fetch:cookiecutter` action will use the
[scaffolder-backend/Cookiecutter](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile)
docker image.
If you are running Backstage from a Docker container and you want to avoid
calling a container inside a container, you can set up Cookiecutter in your own
image, this will use the local installation instead.
You can do so by including the following lines in the last step of your
`Dockerfile`:
```Dockerfile
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip3 install cookiecutter
```
## Customizing the ScaffolderPage with Grouping and Filtering
Once you have more than a few software templates you may want to customize your
+1 -14
View File
@@ -14,25 +14,12 @@ locations like GitHub or GitLab.
When creating custom scaffolder actions, **use camelCase for action IDs** instead of kebab-case. Action IDs with dashes (like `fetch-component-id`) will cause template expressions like `${{ steps.fetch-component-id.output.componentId }}` to return `NaN` because the dashes are evaluated as subtraction operators in JavaScript expressions.
:::note
See the [Writing Custom Actions guide](./writing-custom-actions.md#naming-conventions) and [Template Migration guide](./migrating-from-v1beta2-to-v1beta3.md#watch-out-for-dash-case) for more details.
See the [Writing Custom Actions guide](./writing-custom-actions.md#naming-conventions) for more details.
:::
## Prerequisites
:::note Note
If you're running Backstage with Node 20 or later, you'll need to pass the flag `--no-node-snapshot` to Node in order to
use the templates feature.
One way to do this is to specify the `NODE_OPTIONS` environment variable before starting Backstage:
`export NODE_OPTIONS="${NODE_OPTIONS:-} --no-node-snapshot"`
It's important to append to the existing `NODE_OPTIONS` value, if it's already set, rather than overwriting it, since some NodeJS Debugging tools may rely on this environment variable to work properly.
:::
These docs assume you have already gone over the [Backstage Getting Started](../../getting-started) section and you are able to run Backstage locally or it has been deployed somewhere.
## Getting Started
@@ -1,241 +0,0 @@
---
id: migrating-from-v1beta2-to-v1beta3
title: Migrating to v1beta3 templates
description: How to migrate your existing templates to beta3 syntax
---
# What's new?
Well then, here we are! 🚀
Backstage has had many forms of templating languages throughout different
plugins and different systems. We've had `cookiecutter` syntax in templates, and
we also had `handlebars` templating in the `kind: Template`. Then we wanted to
remove the additional dependency on `cookiecutter` for Software Templates out of
the box, so we introduced `nunjucks` as an alternative in `fetch:template`
action which is based on the `jinja2` syntax so they're pretty similar. In an
effort to reduce confusion and unify on to one templating language, we're
officially deprecating support for `handlebars` templating in the
`kind: Template` entities with `apiVersion` `scaffolder.backstage.io/v1beta3`
and moving to using `nunjucks` instead.
This provides us a lot of built in `filters` (`handlebars` helpers), that as
Template authors will give you much more flexibility out of the box, and also
open up sharing of filters in the Entity and the actual `skeleton` too, and
removing the slight differences between the two languages.
We've also removed a lot of the built in helpers that we shipped with
`handlebars`, as they're now supported as first class citizens by either
`nunjucks` or the new `scaffolder` when using `scaffolder.backstage.io/v1beta3`
`apiVersion`
The migration path is pretty simple, and we've removed some of the pain points
from writing the `handlebars` templates too. Let's go through what's new and how
to upgrade.
## Add the Processor to the `plugin-catalog-backend`
An important change is to add the required processor to your `packages/backend/src/plugins/catalog.ts`
```ts title="packages/backend/src/plugins/catalog.ts"
/* highlight-add-next-line */
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);
/* highlight-add-next-line */
builder.addProcessor(new ScaffolderEntitiesProcessor());
const { processingEngine, router } = await builder.build();
// ..
}
```
## `backstage.io/v1beta2` -> `scaffolder.backstage.io/v1beta3`
The most important change is that you'll need to switch over the `apiVersion` in
your templates to the new one.
```yaml
kind: Template
# highlight-remove-next-line
apiVersion: backstage.io/v1beta2
# highlight-add-next-line
apiVersion: scaffolder.backstage.io/v1beta3
```
## `${{ }}` instead of `"{{ }}"`
One really big readability issue and cause for confusion was the fact that with
`handlebars` and `yaml` you always had to wrap your templating strings in quotes
in `yaml` so that it didn't try to parse it as a `json` object and fail. This
was pretty annoying, as it also meant that all things look like strings. Now
that's no longer the case, you can now remove the `""` and take advantage of
writing nice `yaml` files that just work.
```yaml
spec:
steps:
input:
# highlight-remove-next-line
description: 'This is {{ parameters.name }}'
# highlight-add-next-line
description: This is ${{ parameters.name }}
# highlight-remove-next-line
repoUrl: '{{ parameters.repoUrl }}'
# highlight-add-next-line
repoUrl: ${{ parameters.repoUrl }}
```
## No more `eq` or `not` helpers
These helpers are no longer needed with the more expressive `api` that
`nunjucks` provides. You can simply use the built-in `nunjucks` and `jinja2`
style operators.
```yaml
spec:
steps:
input:
# highlight-remove-next-line
if: '{{ eq parameters.value "backstage" }}'
# highlight-add-next-line
if: ${{ parameters.value === "backstage" }}
```
And then for the `not`
```yaml
spec:
steps:
input:
# highlight-remove-next-line
if: '{{ not parameters.value "backstage" }}'
# highlight-add-next-line
if: ${{ parameters.value !== "backstage" }}
```
Much better right? ✨
## No more `json` helper
This helper is no longer needed, as we've added support for complex values and
supporting the additional primitive values now rather than everything being a
`string`. This means that now that you can pass around `parameters` and it
should all work as expected and keep the type that has been declared in the
input schema.
```yaml
spec:
parameters:
test:
type: number
name: Test Number
address:
type: object
required:
- line1
properties:
line1:
type: string
name: Line 1
line2:
type: string
name: Line 2
steps:
- id: test step
action: run:something
input:
# highlight-remove-next-line
address: '{{ json parameters.address }}'
# highlight-add-next-line
address: ${{ parameters.address }}
# highlight-remove-next-line
test: '{{ parameters.test }}'
# highlight-add-next-line
test: ${{ parameters.test }} # this will now make sure that the type of test is a number 🙏
```
## `parseRepoUrl` is now a `filter`
All calls to `parseRepoUrl` are now a `jinja2` `filter`, which means you'll need
to update the syntax.
```yaml
spec:
steps:
input:
# highlight-remove-next-line
repoUrl: '{{ parseRepoUrl parameters.repoUrl }}'
# highlight-add-next-line
repoUrl: ${{ parameters.repoUrl | parseRepoUrl }}
```
Now we have complex value support here too, expect that this `filter` will go
away in future versions and the `RepoUrlPicker` will return an object so
`parameters.repoUrl` will already be a
`{ host: string; owner: string; repo: string }` 🚀
## Links should be used instead of named outputs
Previously, it was possible to provide links to the frontend using the named output `entityRef` and `remoteUrl`.
These should be moved to `links` under the `output` object instead.
```yaml
output:
# highlight-remove-start
remoteUrl: {{ steps['publish'].output.remoteUrl }}
entityRef: {{ steps['register'].output.entityRef }}
# highlight-remove-end
# highlight-add-start
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
# highlight-add-end
```
## Watch out for `dash-case`
The nunjucks compiler can run into issues if the `id` fields in your template steps use dash characters, since these IDs translate directly to JavaScript object properties when accessed as output. One possible migration path is to use `camelCase` for your action IDs.
```yaml
steps:
# highlight-remove-start
id: my-custom-action
...
id: publish-pull-request
input:
repoUrl: {{ steps.my-custom-action.output.repoUrl }} # Will not recognize 'my-custom-action' as a JS property since it contains dashes!
# highlight-remove-end
steps:
# highlight-add-start
id: myCustomAction
...
id: publishPullRequest
input:
repoUrl: ${{ steps.myCustomAction.output.repoUrl }}
# highlight-add-end
```
Alternatively, it's possible to keep the `dash-case` syntax and use brackets for property access as you would in JavaScript:
```yaml
input:
repoUrl: ${{ steps['my-custom-action'].output.repoUrl }}
```
### Summary
Of course, we're always available on [discord](https://discord.gg/backstage-687207715902193673) if
you're stuck or something's not working as expected. You can also
[raise an issue](https://github.com/backstage/backstage/issues/new/choose) with
feedback or bugs!
@@ -1,138 +0,0 @@
---
id: migrating-to-rjsf-v5
title: 'Migrating to react-jsonschema-form@v5'
description: Docs on migrating to `react-jsonschema-form`@v5 and the new designs
---
:::note Note
If you were previously using the `/alpha` imports to test out the `scaffolder/next` work, those imports have been promoted to the default exports from the respective packages. You should just have to remove the `/alpha` from the import path, and remove the `Next` from the import name. `NextScaffolderPage` -> `ScaffolderPage`, `createNextScaffolderFieldExtension` -> `createScaffolderFieldExtension` etc.
:::
## What's `react-jsonschema-form`?
This library is core to the frontend part of the scaffolder plugin, and is responsible for rendering the form in which developers and end users fill out to meet the `jsonschema` requirement for the parameters section.
Since the initial release of the `scaffolder` plugin, we we're on a pretty old version of `react-jsonschema-form` (v3), which has been pretty outdated as of late. The problem with us just bumping this library was that there are several breaking changes with the new v5 version, which we've tried pretty aggressively not to pass on to our end users for their templates and [Custom Field Extensions](https://backstage.io/docs/features/software-templates/writing-custom-field-extensions/).
We're hoping that by duplicating the types from version 3 of `react-jsonschema-form` and making these the types that we will support even though the underlying library is v5, it should get us through all of the breaking changes without passing that down.
## What's new?
With that in mind, this release has `v5` of `react-jsonschema-form`, and with that comes all the new features and bugfixes in `v4` that we were waiting for - one of the main ones being the ability to use `if / then / else` syntax in the `template.yaml` definitions! 🎉
We've also rebuilt how validation works in the `scaffolder` components, which now means that we've opened the ability to have `async` validation functions in your `Field Extensions`.
Some of the pages have gotten a little bit of an overhaul in terms of UI based on some research and feedback from the community and internally.
- The `TemplateList` page has gotten some new `Card` components which show a little more information than the previous version with a little `material-ui` standards.
- The `WizardPage` has received some new updates with the stepper now running horizontally, and the `Review` step being a dedicated step in the stepper.
- The `OngoingTask` page now does not show the logs by default, and instead has a much cleaner interface for tracking the ongoing steps and the pipeline of actions that are currently showing.
- You can also now provide your own `OutputsComponent` which can be used to render the outputs from an ongoing / completed task in a way that suits your templates the best. For instance, if your template produces `Pull Requests`, it could be useful to render these in an interactive way where you can see the statuses of each of these `Pull Requests` in the `Ongoing Task` page.
There's also a lot of bug fixes, and other things, but these are the main ones that we wanted to highlight.
## How do I upgrade
With the release of [`v1.20.0`](https://github.com/backstage/backstage/releases/tag/v1.20.0) these changes should have been made for you. We're hoping that it should be pretty transparent, and things just work as expected. Please reach out to us on [discord](https://discord.com/invite/MUpMjP2) or in a [issue](https://github.com/backstage/backstage/issues/new?assignees=&labels=bug&projects=&template=bug.yaml&title=%F0%9F%90%9B+Bug+Report%3A+%3Ctitle%3E) if you're having issues.
It's possible that if you have a hard dependency on any of the `@rjsf/*` libraries in your app, you'll need to bump these manually to the version that we currently support: `5.13.6` at the time of writing. There could be breaking changes that you will have to fix here however, which we think should be pretty simple, but they're things like changing imports from `@rjsf/core` to `@rjsf/utils`.
```ts
/* highlight-remove-next-line */
import { FieldValidation } from '@rjsf/core';
/* highlight-add-next-line */
import { FieldValidation } from '@rjsf/utils';
```
## Escape hatch
If for some reason the upgrade to [`v1.20.0`](https://github.com/backstage/backstage/releases/tag/v1.20.0) didn't go as planned, there's an escape hatch for use until the next mainline release in which we will try to get any issues fixed before removing the legacy code.
We've moved some of the older exports to an `/alpha` export so you should be able switch to using the old library just in case.
```tsx
/* highlight-remove-next-line */
import { ScaffolderPage } from '@backstage/plugin-scaffolder';
/* highlight-add-next-line */
import { LegacyScaffolderPage } from '@backstage/plugin-scaffolder/alpha';
```
And this API should be the exact same as the previous Router, so you should be able to make a change like the following further down in this file:
```tsx
<Route
path="/create"
element={
{/* highlight-remove-next-line */}
<ScaffolderPage
{/* highlight-add-next-line */}
<LegacyScaffolderPage
groups={[
{
title: 'Recommended',
filter: entity =>
entity?.metadata?.tags?.includes('recommended') ?? false,
},
]}
/>
}
>
<ScaffolderFieldExtensions>
<LowerCaseValuePickerFieldExtension />
{/* ... other extensions */}
</ScaffolderFieldExtensions>
<ScaffolderLayouts>
<TwoColumnLayout />
{/* ... other layouts */}
</ScaffolderLayouts>
</Route>
```
And you can also update any of your `CustomFieldExtensions` to use the old helper like so:
```ts
/* highlight-remove-next-line */
import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder';
/* highlight-add-next-line */
import { createLegacyScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react/alpha';
export const EntityNamePickerFieldExtension = scaffolderPlugin.provide(
/* highlight-remove-next-line */
createScaffolderFieldExtension({
/* highlight-add-next-line */
createLegacyScaffolderFieldExtension({
component: EntityNamePicker,
name: 'EntityNamePicker',
validation: entityNamePickerValidation,
}),
);
```
And in the component themselves, you might have to do the following:
```tsx
/* highlight-remove-next-line */
import { FieldExtensionComponentProps } from '@backstage/plugin-scaffolder-react';
/* highlight-add-next-line */
import { LegacyFieldExtensionComponentProps } from '@backstage/plugin-scaffolder-react/alpha';
export const EntityNamePicker = (
/* highlight-remove-next-line */
props: FieldExtensionComponentProps<string, EntityNamePickerProps>,
/* highlight-add-next-line */
props: LegacyFieldExtensionComponentProps<string, EntityNamePickerProps>,
) => {
const {
onChange,
required,
schema: { title = 'Name', description = 'Unique name of the component' },
rawErrors,
formData,
idSchema,
placeholder,
} = props;
// ..
};
```
@@ -8,15 +8,6 @@ If you want to extend the functionality of the Scaffolder, you can do so
by writing custom actions which can be used alongside our
[built-in actions](./builtin-actions.md).
:::note Note
When adding custom actions, the actions array will **replace the
built-in actions too**. Meaning, you will no longer be able to use them.
If you want to continue using the builtin actions, include them in the `actions`
array when registering your custom actions, as seen below.
:::
## Streamlining Custom Action Creation with Backstage CLI
The creation of custom actions in Backstage has never been easier thanks to the Backstage CLI. This tool streamlines the
+1 -1
View File
@@ -237,7 +237,7 @@ const config: Config = {
},
{
from: '/docs/features/software-templates/testing-scaffolder-alpha',
to: '/docs/features/software-templates/migrating-to-rjsf-v5',
to: '/docs/features/software-templates/',
},
{
from: '/docs/auth/glossary',
-2
View File
@@ -302,8 +302,6 @@ export default {
'features/software-templates/writing-custom-field-extensions',
'features/software-templates/writing-custom-step-layouts',
'features/software-templates/authorizing-scaffolder-template-details',
'features/software-templates/migrating-to-rjsf-v5',
'features/software-templates/migrating-from-v1beta2-to-v1beta3',
'features/software-templates/dry-run-testing',
'features/software-templates/experimental',
'features/software-templates/templating-extensions',
-1
View File
@@ -80,7 +80,6 @@ nav:
- Writing Custom Actions: 'features/software-templates/writing-custom-actions.md'
- Writing Custom Step Layouts: 'features/software-templates/writing-custom-step-layouts.md'
- Templating Extensions: 'features/software-templates/templating-extensions.md'
- Migrating from v1beta2 to v1beta3 templates: 'features/software-templates/migrating-from-v1beta2-to-v1beta3.md'
- Dry Run Testing: 'features/software-templates/dry-run-testing.md'
- Backstage Search:
- Overview: 'features/search/README.md'