Merge remote-tracking branch 'origin/master' into gitlabissuescreate-idempotent
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend-module-github-provider': patch
|
||||
---
|
||||
|
||||
Fixed a bug where the requested scope was ignored when refreshing sessions for a GitHub OAuth App. This would lead to access tokens being returned that didn't have the requested scope, and in turn errors when trying to use these tokens.
|
||||
|
||||
As part of this fix all existing sessions are being revoked in order to ensure that they receive the correct scope.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Added support for experimental form decorators when dry-running templates in the template editor.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-backstage-openapi': patch
|
||||
'@backstage/plugin-catalog-backend-module-bitbucket-server': patch
|
||||
'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch
|
||||
'@backstage/plugin-catalog-backend-module-msgraph': patch
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
'@backstage/plugin-catalog-backend-module-azure': patch
|
||||
'@backstage/plugin-catalog-backend-module-ldap': patch
|
||||
'@backstage/plugin-catalog-backend-module-aws': patch
|
||||
---
|
||||
|
||||
Minor doc string update
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Improved performance when applying conditional decisions to entities within the permission framework.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
---
|
||||
|
||||
Fixed issue `syncEntityDocs` that would cause the `/sync` endpoint to be continuously called if the request fails
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': minor
|
||||
---
|
||||
|
||||
**BREAKING**: The `remoteUrl` output is no longer required, it can be empty only when using the new `createWhenEmpty` boolean flag.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
'@backstage/plugin-scaffolder-node': minor
|
||||
---
|
||||
|
||||
Added new `createTemplateFilter`, `createTemplateGlobalFunction`, `createTemplateGlobalValue` for template extensions.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Revert client side paginated catalog table to using built in Material Table toolbar component
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Add check to make sure that the `--link` option for the `start` command is a valid workspace.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
'@backstage/cli': minor
|
||||
---
|
||||
|
||||
The `new` command is now powered by a new template system that allows you to define your own templates in a declarative way, as well as import existing templates from external sources. See the [CLI templates documentation](https://backstage.io/docs/tooling/cli/templates) for more information.
|
||||
|
||||
The following flags for the `new` command have been deprecated and will be removed in a future release:
|
||||
|
||||
- `--license=<license>`: Configure the global `license` instead.
|
||||
- `--no-private`: Configure the global `private` instead.
|
||||
- `--baseVersion=<version>`: Configure the global `version` instead.
|
||||
- `--npmRegistry=<url>`: Configure the global `publishRegistry` instead.
|
||||
- `--scope=<scope>`: Configure the global `namePrefix` and/or `namePluginInfix` instead.
|
||||
|
||||
As part of this change the template IDs and their options have changed. The following backwards compatibility mappings for the `--select` and `--option` flags are enabled when using the default set of templates, but they will also be removed in the future:
|
||||
|
||||
- `--select=plugin` is mapped to `--select=frontend-plugin` instead.
|
||||
- `--option=id=<id>` is mapped to `--option=pluginId=<id>` instead.
|
||||
@@ -223,6 +223,7 @@
|
||||
"eleven-mice-sleep",
|
||||
"fair-mangos-sleep",
|
||||
"fair-rocks-dream",
|
||||
"fast-rabbits-unite",
|
||||
"few-ducks-cross",
|
||||
"few-shrimps-kiss",
|
||||
"fifty-humans-repair",
|
||||
@@ -230,20 +231,29 @@
|
||||
"fifty-trains-attend",
|
||||
"fifty-turtles-count",
|
||||
"fluffy-carrots-wink",
|
||||
"fresh-bears-thank",
|
||||
"gentle-actors-sleep",
|
||||
"gold-cameras-battle",
|
||||
"gold-rabbits-change",
|
||||
"gorgeous-shoes-stare",
|
||||
"green-jokes-provide",
|
||||
"grumpy-crews-sneeze",
|
||||
"happy-windows-shop",
|
||||
"heavy-bobcats-search",
|
||||
"heavy-olives-thank",
|
||||
"hip-hotels-type",
|
||||
"hot-mice-cry",
|
||||
"hot-planes-hear",
|
||||
"hungry-chefs-relax",
|
||||
"hungry-cycles-hide",
|
||||
"hungry-mirrors-sniff",
|
||||
"hungry-rings-live",
|
||||
"large-pans-brake",
|
||||
"late-cycles-teach",
|
||||
"little-mangos-reply",
|
||||
"loud-baboons-relate",
|
||||
"loud-walls-build",
|
||||
"lucky-seas-live",
|
||||
"many-goats-flash",
|
||||
"metal-worms-live",
|
||||
"neat-singers-rhyme",
|
||||
@@ -260,7 +270,10 @@
|
||||
"orange-brooms-lick",
|
||||
"plenty-bobcats-drum",
|
||||
"polite-apricots-exercise",
|
||||
"polite-pumas-joke",
|
||||
"pretty-apricots-poke",
|
||||
"pretty-worms-walk",
|
||||
"proud-dolls-behave",
|
||||
"proud-dryers-act",
|
||||
"proud-hornets-cheer",
|
||||
"purple-years-cross",
|
||||
@@ -276,15 +289,24 @@
|
||||
"slow-bananas-beam",
|
||||
"smart-ligers-sniff",
|
||||
"soft-planets-mate",
|
||||
"soft-seahorses-relate",
|
||||
"tall-scissors-sip",
|
||||
"tame-hats-shout",
|
||||
"thirty-dancers-cheat",
|
||||
"three-glasses-sell",
|
||||
"tidy-forks-pay",
|
||||
"tidy-oranges-allow",
|
||||
"tiny-spoons-repeat",
|
||||
"twelve-eyes-stare",
|
||||
"two-schools-deny",
|
||||
"warm-masks-ring",
|
||||
"wet-bees-heal",
|
||||
"wise-apes-juggle",
|
||||
"wise-cycles-sparkle",
|
||||
"witty-clocks-thank",
|
||||
"witty-ducks-cross",
|
||||
"witty-pets-march",
|
||||
"yellow-rules-vanish",
|
||||
"young-plants-report"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
---
|
||||
|
||||
Support file globbing in fs:delete
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-notifications': patch
|
||||
'@backstage/plugin-signals': patch
|
||||
---
|
||||
|
||||
Added support for the new frontend system via `/alpha` export.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-node-test-utils': patch
|
||||
---
|
||||
|
||||
Changing the default behaviour of the checkpoint in createMockActionContext. It will reduce the code boilerplate in the tests where checkpoint is used.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': patch
|
||||
---
|
||||
|
||||
Fix spacing props not being applied for custom values.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-node': patch
|
||||
---
|
||||
|
||||
Added `scopeAlreadyGranted` property to `OAuthAuthenticatorRefreshInput`, signaling to the provider whether the requested scope has already been granted when persisting session scope.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Replaced dependency `node-libs-browser` with `node-stdlib-browser`
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
Add support for defining multiple attachment points for extensions and blueprints.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-unprocessed-entities': patch
|
||||
---
|
||||
|
||||
Added alpha support for the New Frontend System
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Added a configuration to permit backend plugin failures on startup:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
...
|
||||
startup:
|
||||
plugins:
|
||||
plugin-x:
|
||||
onPluginBootFailure: continue
|
||||
```
|
||||
|
||||
This configuration permits `plugin-x` to fail on startup. Omitting the
|
||||
`onPluginBootFailure` configuration matches the previous behavior, wherein any
|
||||
individual plugin failure aborts backend startup.
|
||||
|
||||
The default can also be changed, so that continuing on failure is the default
|
||||
unless otherwise specified:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
startup:
|
||||
default:
|
||||
onPluginBootFailure: continue
|
||||
plugins:
|
||||
catalog:
|
||||
onPluginBootFailure: abort
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': minor
|
||||
---
|
||||
|
||||
Updated core CSS tokens and fixing the Button component accordingly.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Added conditional rendering of `oneOf` output schemas on the Installed Actions page for scaffolder actions
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Use template title for ongoing task page header
|
||||
@@ -157,6 +157,7 @@ Gitiles
|
||||
gitlab
|
||||
GitLab
|
||||
globals
|
||||
globbing
|
||||
Gource
|
||||
Grafana
|
||||
graphql
|
||||
@@ -328,6 +329,7 @@ preconfigured
|
||||
prepack
|
||||
Preprarer
|
||||
productional
|
||||
projectSlug
|
||||
Protobuf
|
||||
proxied
|
||||
proxying
|
||||
@@ -425,6 +427,7 @@ subheaders
|
||||
subkey
|
||||
subpath
|
||||
subroutes
|
||||
substring
|
||||
subtree
|
||||
superfences
|
||||
Superfences
|
||||
|
||||
@@ -3,8 +3,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '.github/workflows/sync_version-packages.yml'
|
||||
|
||||
concurrency:
|
||||
group: sync-version-packages
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version: 20.x
|
||||
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5
|
||||
- uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"build": "npm run build:css && next build",
|
||||
"build:css": "node scripts/build-css.js",
|
||||
"lint": "next lint",
|
||||
"start": "next dev"
|
||||
"start": "npm run build:css && next dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-sass": "^6.0.2",
|
||||
@@ -17,6 +17,7 @@
|
||||
"@storybook/react": "^8.4.7",
|
||||
"@uiw/codemirror-themes": "^4.23.7",
|
||||
"@uiw/react-codemirror": "^4.23.7",
|
||||
"motion": "^12.4.1",
|
||||
"next": "14.2.23",
|
||||
"react": "^18.0.2",
|
||||
"react-dom": "^18.0.2",
|
||||
|
||||
@@ -9,11 +9,6 @@ const source1 = path.join(__dirname, `${source}/core.css`);
|
||||
const destination1 = path.join(__dirname, `${destination}/core.css`);
|
||||
const source2 = path.join(__dirname, `${source}/components.css`);
|
||||
const destination2 = path.join(__dirname, `${destination}/components.css`);
|
||||
const source3 = path.join(
|
||||
__dirname,
|
||||
`../../packages/canon/.storybook/themes/backstage.css`,
|
||||
);
|
||||
const destination3 = path.join(__dirname, `${destination}/backstage.css`);
|
||||
|
||||
// Function to bundle and copy the CSS file
|
||||
const bundleAndCopyFile = async (source, destination) => {
|
||||
@@ -34,7 +29,6 @@ const bundleAndCopyFile = async (source, destination) => {
|
||||
Promise.all([
|
||||
bundleAndCopyFile(source1, destination1),
|
||||
bundleAndCopyFile(source2, destination2),
|
||||
bundleAndCopyFile(source3, destination3),
|
||||
])
|
||||
.then(() => {
|
||||
// Add an empty line after all operations are complete - It looks better in the terminal :)
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ButtonResponsive,
|
||||
} from '@/snippets/button';
|
||||
import { buttonVariants } from '@/snippets/_snippets';
|
||||
import { buttonPropDefs } from '../../../../../../packages/canon/src/components/Button/Button.props';
|
||||
|
||||
# Button
|
||||
|
||||
@@ -58,34 +59,7 @@ A button component that can be used to trigger actions.
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable
|
||||
data={{
|
||||
size: {
|
||||
type: ['small', 'medium'],
|
||||
responsive: true,
|
||||
},
|
||||
variant: {
|
||||
type: ['primary', 'secondary', 'tertiary'],
|
||||
responsive: true,
|
||||
},
|
||||
disabled: {
|
||||
type: 'boolean',
|
||||
responsive: false,
|
||||
},
|
||||
children: {
|
||||
type: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
type: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<PropsTable data={buttonPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ are prefixed with `fg` to make it easier to identify.
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-text-primary</Chip>
|
||||
<Chip head>--canon-fg-primary</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
|
||||
@@ -194,7 +194,7 @@ are prefixed with `fg` to make it easier to identify.
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-fg-text-secondary</Chip>
|
||||
<Chip head>--canon-fg-secondary</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
|
||||
@@ -280,7 +280,7 @@ low contrast to help as a separator with the different background colors.
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-border-focus</Chip>
|
||||
<Chip head>--canon-border-pressed</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Used when the component is interactive and focused.
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
.breakpointContent {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--canon-border);
|
||||
background-color: var(--canon-bg);
|
||||
border: 1px solid var(--border);
|
||||
background-color: var(--bg);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ import { Sidebar } from '../components/Sidebar';
|
||||
import { Toolbar } from '@/components/Toolbar';
|
||||
import { Providers } from './providers';
|
||||
import { CustomTheme } from '@/components/CustomTheme';
|
||||
import styles from './page.module.css';
|
||||
import styles from '../css/page.module.css';
|
||||
|
||||
import './globals.css';
|
||||
import '../css/globals.css';
|
||||
import '/public/core.css';
|
||||
import '/public/components.css';
|
||||
import '/public/backstage.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Canon',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.codeBlock {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--canon-border);
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
background: transparent;
|
||||
overflow-x: auto;
|
||||
@@ -15,10 +15,10 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 12px 20px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--canon-fg-text-secondary);
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.code {
|
||||
|
||||
@@ -5,7 +5,7 @@ import CodeMirror from '@uiw/react-codemirror';
|
||||
import { sass } from '@codemirror/lang-sass';
|
||||
import styles from './styles.module.css';
|
||||
import { usePlayground } from '@/utils/playground-context';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { AnimatePresence, motion } from 'motion/react';
|
||||
import { Icon } from '../../../../packages/canon';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
right: 16px;
|
||||
width: 240px;
|
||||
height: 47px;
|
||||
background-color: var(--canon-bg-elevated);
|
||||
background-color: var(--panel);
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid var(--canon-border);
|
||||
border: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
@@ -31,8 +31,8 @@
|
||||
.header {
|
||||
height: 46px;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
border-bottom: 1px solid var(--border);
|
||||
background-color: var(--panel);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -65,8 +65,8 @@
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
color: #fff;
|
||||
background-color: var(--canon-bg);
|
||||
color: var(--canon-fg-text-primary);
|
||||
background-color: var(--bg);
|
||||
color: var(--primary);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
.container {
|
||||
display: flex;
|
||||
background-color: var(--canon-bg-elevated);
|
||||
background-color: var(--panel);
|
||||
padding: var(--canon-space-6);
|
||||
border-radius: var(--canon-radius-2);
|
||||
border: 1px solid var(--canon-border);
|
||||
border: 1px solid var(--border);
|
||||
margin-bottom: var(--canon-space-6);
|
||||
gap: var(--canon-space-6);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.icon path {
|
||||
fill: var(--canon-fg-text-primary);
|
||||
fill: var(--canon-fg-primary);
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -29,14 +29,14 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background-color: var(--canon-bg);
|
||||
border: 1px solid var(--canon-border);
|
||||
background-color: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--canon-radius-2);
|
||||
padding: 0 var(--canon-space-3);
|
||||
height: 28px;
|
||||
border-radius: 100px;
|
||||
margin-top: var(--canon-space-3);
|
||||
font-size: var(--canon-font-size-3);
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
gap: var(--canon-space-2);
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
height: 80px;
|
||||
border: 1px solid #d3d3d3;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--canon-bg-elevated);
|
||||
background-color: var(--panel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import { icons } from '../../../../packages/canon';
|
||||
|
||||
// Define a more specific type for the data object
|
||||
type PropData = {
|
||||
type: string | string[];
|
||||
responsive: boolean;
|
||||
values?: string | string[];
|
||||
responsive?: boolean;
|
||||
default?: string;
|
||||
};
|
||||
|
||||
// Modify the PropsTable component to use the new type
|
||||
@@ -16,17 +17,34 @@ export const PropsTable = <T extends Record<string, PropData>>({
|
||||
}: {
|
||||
data: T;
|
||||
}) => {
|
||||
const completeData = {
|
||||
...data,
|
||||
children: {
|
||||
values: 'ReactNode',
|
||||
responsive: false,
|
||||
},
|
||||
className: {
|
||||
values: 'string',
|
||||
responsive: false,
|
||||
},
|
||||
style: {
|
||||
values: 'CSSProperties',
|
||||
responsive: false,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.HeaderRow>
|
||||
<Table.HeaderCell>Prop</Table.HeaderCell>
|
||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||
<Table.HeaderCell>Default</Table.HeaderCell>
|
||||
<Table.HeaderCell>Responsive</Table.HeaderCell>
|
||||
</Table.HeaderRow>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{Object.keys(data).map(n => (
|
||||
{Object.keys(completeData).map(n => (
|
||||
<Table.Row key={n}>
|
||||
<Table.Cell>
|
||||
<Chip head>{n}</Chip>
|
||||
@@ -35,17 +53,22 @@ export const PropsTable = <T extends Record<string, PropData>>({
|
||||
<div
|
||||
style={{ display: 'flex', flexWrap: 'wrap', gap: '0.375rem' }}
|
||||
>
|
||||
{data[n].type === 'icon' ? (
|
||||
{completeData[n].values === 'icon' ? (
|
||||
Object.keys(icons).map(icon => <Chip key={icon}>{icon}</Chip>)
|
||||
) : Array.isArray(data[n].type) ? (
|
||||
data[n].type.map(t => <Chip key={t}>{t}</Chip>)
|
||||
) : Array.isArray(completeData[n].values) ? (
|
||||
completeData[n].values.map(t => <Chip key={t}>{t}</Chip>)
|
||||
) : (
|
||||
<Chip>{data[n].type}</Chip>
|
||||
<Chip>{completeData[n].values}</Chip>
|
||||
)}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{data[n].responsive ? 'Yes' : 'No'}</Chip>
|
||||
<Chip>
|
||||
{completeData[n].default ? completeData[n].default : '-'}
|
||||
</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Chip>{completeData[n].responsive ? 'Yes' : 'No'}</Chip>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
|
||||
@@ -6,27 +6,39 @@
|
||||
.sidebar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
border-radius: 8px;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
color: var(--canon-fg-text-primary);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
border-right: 1px solid var(--canon-border);
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
height: calc(100vh - 32px);
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.025);
|
||||
color: var(--primary);
|
||||
background-color: var(--panel);
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
overflow: scroll;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
|
||||
.logoContainer {
|
||||
padding-left: 6px;
|
||||
padding-top: 32px;
|
||||
}
|
||||
|
||||
.logo path {
|
||||
fill: var(--canon-fg-text-primary);
|
||||
fill: var(--primary);
|
||||
}
|
||||
|
||||
.menu {
|
||||
@@ -43,10 +55,11 @@
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
padding: 12px 0;
|
||||
color: var(--primary);
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.line {
|
||||
@@ -63,23 +76,57 @@
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
}
|
||||
}
|
||||
|
||||
.line.active {
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
.line.active .lineTitle {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.lineTitle {
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.lineStatus {
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-3);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
width: 0.25rem;
|
||||
border-radius: 0.375rem;
|
||||
margin: 0.5rem;
|
||||
opacity: 0;
|
||||
transition: opacity 150ms 300ms;
|
||||
right: -20px;
|
||||
|
||||
&[data-hovering],
|
||||
&[data-scrolling] {
|
||||
opacity: 1;
|
||||
transition-duration: 75ms;
|
||||
transition-delay: 0ms;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 1.25rem;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 100%;
|
||||
border-radius: inherit;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
import { components, overview, layoutComponents, theme } from '@/utils/data';
|
||||
import { Box } from '../../../../packages/canon';
|
||||
import { motion } from 'framer-motion';
|
||||
import { motion } from 'motion/react';
|
||||
import styles from './Sidebar.module.css';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Fragment } from 'react';
|
||||
@@ -53,9 +52,8 @@ export const Docs = () => {
|
||||
{data.map(section => {
|
||||
return (
|
||||
<Fragment key={section.title}>
|
||||
<Box marginTop="lg" marginBottom="2xs">
|
||||
<div className={styles.sectionTitle}>{section.title}</div>
|
||||
</Box>
|
||||
<div className={styles.sectionTitle}>{section.title}</div>
|
||||
|
||||
{section.content.map(item => {
|
||||
const isActive = pathname === `${section.url}/${item.slug}`;
|
||||
|
||||
|
||||
@@ -2,28 +2,38 @@ import styles from './Sidebar.module.css';
|
||||
import { Docs } from './docs';
|
||||
import { Playground } from './playground';
|
||||
import Link from 'next/link';
|
||||
import { ScrollArea } from '@base-ui-components/react/scroll-area';
|
||||
|
||||
export const Sidebar = () => {
|
||||
return (
|
||||
<div className={styles.sidebar}>
|
||||
<div className={styles.logoContainer}>
|
||||
<Link href="/">
|
||||
<svg
|
||||
width="89"
|
||||
height="27"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={styles.logo}
|
||||
>
|
||||
<path d="M77.414 9.71h3.036v2.992l-.264-.022c.279-.88.748-1.65 1.408-2.31.675-.66 1.533-.99 2.574-.99 1.086 0 1.9.352 2.442 1.056.558.689.836 1.598.836 2.728v7.502H84.41v-6.6c0-.66-.117-1.159-.352-1.496-.234-.338-.623-.506-1.166-.506-.601 0-1.122.271-1.562.814a4.321 4.321 0 0 0-.88 1.848v5.94h-3.036V9.71ZM69.868 21.04c-1.13 0-2.142-.257-3.036-.77a5.547 5.547 0 0 1-2.068-2.09c-.484-.895-.726-1.892-.726-2.992 0-1.115.242-2.112.726-2.992a5.503 5.503 0 0 1 2.068-2.112c.88-.514 1.892-.77 3.036-.77 1.144 0 2.156.256 3.036.77a5.347 5.347 0 0 1 2.046 2.112c.498.88.748 1.87.748 2.97 0 1.114-.25 2.12-.748 3.014a5.343 5.343 0 0 1-2.068 2.09c-.88.513-1.885.77-3.014.77Zm0-2.618c.557 0 1.048-.132 1.474-.396a2.79 2.79 0 0 0 .99-1.144c.234-.499.352-1.064.352-1.694 0-.63-.118-1.188-.352-1.672a2.764 2.764 0 0 0-.99-1.166c-.426-.279-.917-.418-1.474-.418-.558 0-1.05.14-1.474.418a2.764 2.764 0 0 0-.99 1.166c-.22.484-.33 1.041-.33 1.672 0 .63.11 1.195.33 1.694.234.484.564.865.99 1.144.425.264.916.396 1.474.396ZM52.385 9.71h3.036v2.992l-.264-.022c.279-.88.748-1.65 1.408-2.31.675-.66 1.533-.99 2.574-.99 1.085 0 1.9.352 2.442 1.056.557.689.836 1.598.836 2.728v7.502h-3.036v-6.6c0-.66-.117-1.159-.352-1.496-.235-.338-.623-.506-1.166-.506-.601 0-1.122.271-1.562.814a4.321 4.321 0 0 0-.88 1.848v5.94h-3.036V9.71ZM43.904 20.952c-1.026 0-1.87-.308-2.53-.924-.66-.616-.99-1.416-.99-2.398 0-1.115.418-2.01 1.254-2.684.85-.69 2.105-1.034 3.762-1.034h2.75l-.924.352v-.462c0-.572-.176-1.02-.528-1.342-.352-.338-.931-.506-1.738-.506-.69 0-1.386.154-2.09.462-.704.308-1.29.69-1.76 1.144v-2.882c.47-.352 1.1-.66 1.892-.924a7.942 7.942 0 0 1 2.486-.396c1.599 0 2.78.41 3.542 1.232.778.806 1.166 1.914 1.166 3.322v5.742c0 .176.008.352.022.528.03.161.066.322.11.484h-3.014v-3.3l.044 1.386a4.044 4.044 0 0 1-1.32 1.606c-.572.396-1.283.594-2.134.594Zm.858-2.156c.558 0 1.049-.206 1.474-.616.44-.426.77-.954.99-1.584v-.792h-1.562c-.762 0-1.334.146-1.716.44-.381.278-.572.674-.572 1.188 0 .41.125.74.374.99.264.25.602.374 1.012.374ZM34.66 21.04c-1.525 0-2.874-.345-4.047-1.034-1.174-.704-2.09-1.68-2.75-2.926-.646-1.247-.968-2.662-.968-4.246 0-1.584.322-2.992.968-4.224.66-1.232 1.576-2.193 2.75-2.882 1.173-.704 2.522-1.056 4.048-1.056.968 0 1.818.11 2.552.33.748.22 1.356.506 1.826.858v3.498c-.396-.484-.96-.895-1.694-1.232-.719-.338-1.51-.506-2.376-.506-.939 0-1.775.22-2.508.66-.719.44-1.284 1.056-1.694 1.848-.396.792-.594 1.694-.594 2.706 0 1.026.198 1.943.594 2.75.41.792.975 1.408 1.694 1.848.733.44 1.57.66 2.508.66.88 0 1.68-.162 2.398-.484.718-.323 1.276-.726 1.672-1.21v3.41a5.686 5.686 0 0 1-1.87.902c-.704.22-1.54.33-2.508.33ZM11.275 6.271c.276.526.019 1.152-.464 1.498a5.846 5.846 0 0 0 1.004 10.082c.454.205.789.632.789 1.13v6.206a.837.837 0 0 1-.838.837C5.33 26.024.112 20.804.112 14.366V.862C.112.399.487.024.95.024c4.483 0 8.376 2.533 10.325 6.247Z" />
|
||||
<path d="M14.037 16.729a4.237 4.237 0 0 0 4.234-4.24 4.237 4.237 0 0 0-4.234-4.242 4.237 4.237 0 0 0-4.234 4.241 4.237 4.237 0 0 0 4.234 4.24Z" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.menu}>
|
||||
<Docs />
|
||||
<Playground />
|
||||
</div>
|
||||
<ScrollArea.Root className={styles.root}>
|
||||
<ScrollArea.Viewport className={styles.viewport}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.logoContainer}>
|
||||
<Link href="/">
|
||||
<svg
|
||||
width="89"
|
||||
height="27"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={styles.logo}
|
||||
>
|
||||
<path d="M77.414 9.71h3.036v2.992l-.264-.022c.279-.88.748-1.65 1.408-2.31.675-.66 1.533-.99 2.574-.99 1.086 0 1.9.352 2.442 1.056.558.689.836 1.598.836 2.728v7.502H84.41v-6.6c0-.66-.117-1.159-.352-1.496-.234-.338-.623-.506-1.166-.506-.601 0-1.122.271-1.562.814a4.321 4.321 0 0 0-.88 1.848v5.94h-3.036V9.71ZM69.868 21.04c-1.13 0-2.142-.257-3.036-.77a5.547 5.547 0 0 1-2.068-2.09c-.484-.895-.726-1.892-.726-2.992 0-1.115.242-2.112.726-2.992a5.503 5.503 0 0 1 2.068-2.112c.88-.514 1.892-.77 3.036-.77 1.144 0 2.156.256 3.036.77a5.347 5.347 0 0 1 2.046 2.112c.498.88.748 1.87.748 2.97 0 1.114-.25 2.12-.748 3.014a5.343 5.343 0 0 1-2.068 2.09c-.88.513-1.885.77-3.014.77Zm0-2.618c.557 0 1.048-.132 1.474-.396a2.79 2.79 0 0 0 .99-1.144c.234-.499.352-1.064.352-1.694 0-.63-.118-1.188-.352-1.672a2.764 2.764 0 0 0-.99-1.166c-.426-.279-.917-.418-1.474-.418-.558 0-1.05.14-1.474.418a2.764 2.764 0 0 0-.99 1.166c-.22.484-.33 1.041-.33 1.672 0 .63.11 1.195.33 1.694.234.484.564.865.99 1.144.425.264.916.396 1.474.396ZM52.385 9.71h3.036v2.992l-.264-.022c.279-.88.748-1.65 1.408-2.31.675-.66 1.533-.99 2.574-.99 1.085 0 1.9.352 2.442 1.056.557.689.836 1.598.836 2.728v7.502h-3.036v-6.6c0-.66-.117-1.159-.352-1.496-.235-.338-.623-.506-1.166-.506-.601 0-1.122.271-1.562.814a4.321 4.321 0 0 0-.88 1.848v5.94h-3.036V9.71ZM43.904 20.952c-1.026 0-1.87-.308-2.53-.924-.66-.616-.99-1.416-.99-2.398 0-1.115.418-2.01 1.254-2.684.85-.69 2.105-1.034 3.762-1.034h2.75l-.924.352v-.462c0-.572-.176-1.02-.528-1.342-.352-.338-.931-.506-1.738-.506-.69 0-1.386.154-2.09.462-.704.308-1.29.69-1.76 1.144v-2.882c.47-.352 1.1-.66 1.892-.924a7.942 7.942 0 0 1 2.486-.396c1.599 0 2.78.41 3.542 1.232.778.806 1.166 1.914 1.166 3.322v5.742c0 .176.008.352.022.528.03.161.066.322.11.484h-3.014v-3.3l.044 1.386a4.044 4.044 0 0 1-1.32 1.606c-.572.396-1.283.594-2.134.594Zm.858-2.156c.558 0 1.049-.206 1.474-.616.44-.426.77-.954.99-1.584v-.792h-1.562c-.762 0-1.334.146-1.716.44-.381.278-.572.674-.572 1.188 0 .41.125.74.374.99.264.25.602.374 1.012.374ZM34.66 21.04c-1.525 0-2.874-.345-4.047-1.034-1.174-.704-2.09-1.68-2.75-2.926-.646-1.247-.968-2.662-.968-4.246 0-1.584.322-2.992.968-4.224.66-1.232 1.576-2.193 2.75-2.882 1.173-.704 2.522-1.056 4.048-1.056.968 0 1.818.11 2.552.33.748.22 1.356.506 1.826.858v3.498c-.396-.484-.96-.895-1.694-1.232-.719-.338-1.51-.506-2.376-.506-.939 0-1.775.22-2.508.66-.719.44-1.284 1.056-1.694 1.848-.396.792-.594 1.694-.594 2.706 0 1.026.198 1.943.594 2.75.41.792.975 1.408 1.694 1.848.733.44 1.57.66 2.508.66.88 0 1.68-.162 2.398-.484.718-.323 1.276-.726 1.672-1.21v3.41a5.686 5.686 0 0 1-1.87.902c-.704.22-1.54.33-2.508.33ZM11.275 6.271c.276.526.019 1.152-.464 1.498a5.846 5.846 0 0 0 1.004 10.082c.454.205.789.632.789 1.13v6.206a.837.837 0 0 1-.838.837C5.33 26.024.112 20.804.112 14.366V.862C.112.399.487.024.95.024c4.483 0 8.376 2.533 10.325 6.247Z" />
|
||||
<path d="M14.037 16.729a4.237 4.237 0 0 0 4.234-4.24 4.237 4.237 0 0 0-4.234-4.242 4.237 4.237 0 0 0-4.234 4.241 4.237 4.237 0 0 0 4.234 4.24Z" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.menu}>
|
||||
<Docs />
|
||||
<Playground />
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea.Viewport>
|
||||
<ScrollArea.Scrollbar className={styles.scrollbar}>
|
||||
<ScrollArea.Thumb className={styles.thumb} />
|
||||
</ScrollArea.Scrollbar>
|
||||
</ScrollArea.Root>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { components } from '@/utils/data';
|
||||
import { Box } from '../../../../packages/canon/src/components/Box';
|
||||
import { Checkbox } from '../../../../packages/canon/src/components/Checkbox';
|
||||
import { Text } from '../../../../packages/canon/src/components/Text';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Checkbox } from '../../../../packages/canon';
|
||||
import { motion } from 'motion/react';
|
||||
import styles from './Sidebar.module.css';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { screenSizes } from '@/utils/data';
|
||||
@@ -54,28 +52,20 @@ export const Playground = () => {
|
||||
transition={{ duration: 0.2 }}
|
||||
style={{ position: 'absolute' }}
|
||||
>
|
||||
<Box marginTop="lg" marginBottom="2xs">
|
||||
<Text variant="body" weight="bold">
|
||||
Components
|
||||
</Text>
|
||||
</Box>
|
||||
<div className={styles.sectionTitle}>Components</div>
|
||||
{components.map(({ slug, title }) => (
|
||||
<div className={styles.line} key={slug}>
|
||||
<Text variant="body">{title}</Text>
|
||||
<div className={styles.lineTitle}>{title}</div>
|
||||
<Checkbox
|
||||
checked={selectedComponents.includes(slug)}
|
||||
onChange={() => handleComponentCheckboxChange(slug)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Box marginTop="lg" marginBottom="2xs">
|
||||
<Text variant="body" weight="bold">
|
||||
Screen sizes
|
||||
</Text>
|
||||
</Box>
|
||||
<div className={styles.sectionTitle}> Screen sizes</div>
|
||||
{screenSizes.map(({ slug, title }) => (
|
||||
<div className={styles.line} key={slug}>
|
||||
<Text variant="body">{title}</Text>
|
||||
<div className={styles.lineTitle}>{title}</div>
|
||||
<Checkbox
|
||||
checked={selectedScreenSizes.includes(slug)}
|
||||
onChange={() => handleCheckboxChange(slug)}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
.preview {
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
background-color: var(--canon-bg);
|
||||
box-shadow: inset 0 0 0 1px var(--border);
|
||||
background-color: var(--bg);
|
||||
transition: all 0.2s ease-in-out;
|
||||
padding: 1px;
|
||||
position: relative;
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
[data-theme='dark'] .previewContent {
|
||||
background-image: radial-gradient(
|
||||
rgba(255, 255, 255, 0.1) 1px,
|
||||
rgba(255, 255, 255, 0.14) 1px,
|
||||
transparent 0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
.wrapper {
|
||||
border: 1px solid var(--canon-border);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 1rem;
|
||||
@@ -33,7 +33,7 @@
|
||||
padding: 12px 16px !important;
|
||||
border: none !important;
|
||||
text-align: left;
|
||||
background-color: var(--canon-bg-elevated) !important;
|
||||
background-color: var(--panel) !important;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
@@ -43,14 +43,14 @@
|
||||
}
|
||||
|
||||
.tableHeaderCell {
|
||||
border-bottom: 1px solid var(--canon-border) !important;
|
||||
border-bottom: 1px solid var(--border) !important;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tableRow {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
border-bottom: 1px solid var(--border);
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
@@ -59,7 +59,7 @@
|
||||
.tableChip {
|
||||
display: inline-block;
|
||||
font-size: 14px !important;
|
||||
border: 1px solid var(--canon-border);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 0px 6px;
|
||||
height: 24px;
|
||||
|
||||
@@ -33,9 +33,7 @@ export const Tab = (props: React.ComponentProps<typeof TabsPrimitive.Tab>) => (
|
||||
weight="bold"
|
||||
{...rest}
|
||||
style={{
|
||||
color: state.selected
|
||||
? 'var(--canon-fg-text-primary)'
|
||||
: 'var(--canon-fg-text-secondary)',
|
||||
color: state.selected ? 'var(--primary)' : 'var(--secondary)',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
.list {
|
||||
display: flex;
|
||||
gap: var(--canon-space-6);
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: relative;
|
||||
margin-bottom: var(--canon-space-6);
|
||||
}
|
||||
@@ -22,6 +22,6 @@
|
||||
left: var(--active-tab-left);
|
||||
width: var(--active-tab-width);
|
||||
height: 1px;
|
||||
background-color: var(--canon-fg-text-primary);
|
||||
background-color: var(--primary);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
.tabsTheme {
|
||||
width: 142px;
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -26,22 +26,21 @@
|
||||
.tab {
|
||||
all: unset;
|
||||
height: 60px;
|
||||
font-family: var(--docs-font);
|
||||
color: var(--canon-fg-text-secondary);
|
||||
color: var(--secondary);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
&[data-selected] {
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
|
||||
& p {
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,14 +52,14 @@
|
||||
position: absolute;
|
||||
inset: 0.25rem 0;
|
||||
border-radius: 0.25rem;
|
||||
outline: 2px solid var(--canon-bg-elevated);
|
||||
outline: 2px solid var(--panel);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab p {
|
||||
color: var(--canon-fg-text-secondary) !important;
|
||||
color: var(--secondary) !important;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
@@ -72,7 +71,7 @@
|
||||
width: var(--active-tab-width);
|
||||
height: 1px;
|
||||
border-radius: 0.25rem;
|
||||
background-color: var(--canon-fg-text-primary);
|
||||
background-color: var(--primary);
|
||||
transition-property: translate, width, background-color;
|
||||
transition-duration: 200ms;
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
.toolbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
top: 16px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-radius: 8px;
|
||||
z-index: 10;
|
||||
background-color: var(--canon-bg-elevated);
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
background-color: var(--panel);
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 var(--canon-space-6);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
color: var(--color-gray-900);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&:focus-visible {
|
||||
@@ -43,9 +43,9 @@
|
||||
box-sizing: border-box;
|
||||
padding-block: 0.25rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--canon-bg-elevated);
|
||||
background-color: var(--panel);
|
||||
color: var(--color-gray-900);
|
||||
border: 1px solid var(--canon-border);
|
||||
border: 1px solid var(--border);
|
||||
padding-inline: 0.25rem;
|
||||
transform-origin: var(--transform-origin);
|
||||
transition: transform 150ms, opacity 150ms;
|
||||
@@ -104,7 +104,7 @@
|
||||
z-index: 0;
|
||||
position: relative;
|
||||
color: var(--color-gray-50);
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
&[data-highlighted]::before {
|
||||
|
||||
@@ -6,8 +6,7 @@ import { Icon } from '@backstage/canon';
|
||||
import { usePlayground } from '@/utils/playground-context';
|
||||
|
||||
const themes = [
|
||||
{ name: 'Backstage Default', value: 'default' },
|
||||
{ name: 'Backstage Legacy', value: 'legacy' },
|
||||
{ name: 'Backstage', value: 'default' },
|
||||
{ name: 'Custom theme', value: 'custom' },
|
||||
];
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
.tabs {
|
||||
border-radius: 0.375rem;
|
||||
width: 100%;
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tabsTheme {
|
||||
width: 100px;
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--canon-bg);
|
||||
background-color: var(--bg);
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -29,23 +29,23 @@
|
||||
outline: 0;
|
||||
background: none;
|
||||
appearance: none;
|
||||
color: var(--canon-fg-text-secondary);
|
||||
color: var(--secondary);
|
||||
user-select: none;
|
||||
height: 2rem;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
|
||||
&[data-selected] {
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
|
||||
& p {
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,14 +57,14 @@
|
||||
position: absolute;
|
||||
inset: 0.25rem 0;
|
||||
border-radius: 0.25rem;
|
||||
outline: 2px solid var(--canon-bg-elevated);
|
||||
outline: 2px solid var(--panel);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab p {
|
||||
color: var(--canon-fg-text-secondary) !important;
|
||||
color: var(--secondary) !important;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
@@ -76,7 +76,7 @@
|
||||
width: var(--active-tab-width);
|
||||
height: 1.5rem;
|
||||
border-radius: 0.25rem;
|
||||
background-color: var(--canon-bg-elevated);
|
||||
background-color: var(--panel);
|
||||
transition-property: translate, width, background-color;
|
||||
transition-duration: 200ms;
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
:root {
|
||||
--bg: #f8f8f8;
|
||||
--panel: #fff;
|
||||
--primary: #000;
|
||||
--secondary: #757575;
|
||||
--font-regular: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-mono: ui-monospace, 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono',
|
||||
'Courier New', monospace;
|
||||
--font-weight-regular: 400;
|
||||
--font-weight-bold: 600;
|
||||
--border: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
--bg: #000;
|
||||
--panel: #222222;
|
||||
--primary: #fff;
|
||||
--secondary: #9e9e9e;
|
||||
--border: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: var(--canon-bg);
|
||||
color: var(--canon-fg-text-primary);
|
||||
background-color: var(--bg);
|
||||
color: var(--primary);
|
||||
font-family: var(--font-regular);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
--docs-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
||||
iframe {
|
||||
@@ -22,7 +42,7 @@ iframe {
|
||||
.shiki,
|
||||
.shiki span {
|
||||
background-color: transparent !important;
|
||||
font-family: var(--canon-font-monospace);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
@@ -41,8 +61,8 @@ iframe {
|
||||
}
|
||||
|
||||
.ͼ2 .cm-gutters {
|
||||
background-color: var(--canon-bg);
|
||||
border-right: 1px solid var(--canon-border);
|
||||
background-color: var(--bg);
|
||||
border-right: 1px solid var(--border);
|
||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
.h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 4rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.h3 {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.p:first-of-type {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
@@ -4,11 +4,12 @@
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
padding-left: 300px;
|
||||
padding-left: 332px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +18,7 @@
|
||||
}
|
||||
|
||||
.page p {
|
||||
color: var(--canon-fg-text-primary);
|
||||
color: var(--primary);
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 0;
|
||||
@@ -2,66 +2,22 @@ import React, { ReactNode } from 'react';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
import Image, { ImageProps } from 'next/image';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Box } from '../../packages/canon/src/components/Box';
|
||||
import styles from './css/mdx.module.css';
|
||||
|
||||
export function useMDXComponents(components: MDXComponents): MDXComponents {
|
||||
return {
|
||||
h1: ({ children }) => (
|
||||
<Box style={{ marginTop: '4rem' }}>
|
||||
<h1
|
||||
style={{
|
||||
fontFamily: 'var(--docs-font)',
|
||||
fontSize: '3rem',
|
||||
fontWeight: 'var(--canon-font-weight-bold)',
|
||||
marginTop: '4rem',
|
||||
marginBottom: '0.5rem',
|
||||
}}
|
||||
>
|
||||
{children as ReactNode}
|
||||
</h1>
|
||||
</Box>
|
||||
<h1 className={styles.h1}>{children as ReactNode}</h1>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<Box marginTop="2xl" marginBottom="md">
|
||||
<h2
|
||||
style={{
|
||||
fontFamily: 'var(--docs-font)',
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 'var(--canon-font-weight-bold)',
|
||||
}}
|
||||
>
|
||||
{children as ReactNode}
|
||||
</h2>
|
||||
</Box>
|
||||
<h2 className={styles.h2}>{children as ReactNode}</h2>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<h3
|
||||
style={{
|
||||
fontFamily: 'var(--docs-font)',
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 'var(--canon-font-weight-bold)',
|
||||
marginTop: '2rem',
|
||||
marginBottom: '0.5rem',
|
||||
}}
|
||||
>
|
||||
{children as ReactNode}
|
||||
</h3>
|
||||
),
|
||||
p: ({ children }) => (
|
||||
<p
|
||||
style={{
|
||||
fontFamily: 'var(--docs-font)',
|
||||
fontSize: '1rem',
|
||||
lineHeight: '1.5',
|
||||
marginTop: '0',
|
||||
marginBottom: '1rem',
|
||||
}}
|
||||
>
|
||||
{children as ReactNode}
|
||||
</p>
|
||||
<h3 className={styles.h3}>{children as ReactNode}</h3>
|
||||
),
|
||||
p: ({ children }) => <p className={styles.p}>{children as ReactNode}</p>,
|
||||
a: ({ children, href }) => (
|
||||
<a href={href} style={{ color: 'var(--canon-fg-text-primary)' }}>
|
||||
<a href={href} style={{ color: 'var(--primary)' }}>
|
||||
{children as ReactNode}
|
||||
</a>
|
||||
),
|
||||
@@ -78,12 +34,12 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
||||
code: ({ children }) => (
|
||||
<code
|
||||
style={{
|
||||
fontFamily: 'var(--canon-font-monospace)',
|
||||
backgroundColor: 'var(--canon-bg-elevated)',
|
||||
fontFamily: 'var(--font-mono)',
|
||||
backgroundColor: 'var(--panel)',
|
||||
padding: '0.2rem 0.375rem',
|
||||
borderRadius: '0.25rem',
|
||||
color: 'var(--canon-fg-text-secondary)',
|
||||
border: '1px solid var(--canon-border)',
|
||||
color: 'var(--secondary)',
|
||||
border: '1px solid var(--border)',
|
||||
fontSize: '0.875rem',
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -11,9 +11,6 @@ export const ButtonPreview = () => {
|
||||
<Button iconStart="cloud" variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart="cloud" variant="tertiary">
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Box, Container } from '../../../packages/canon';
|
||||
|
||||
const DecorativeBox = () => (
|
||||
<Box
|
||||
borderRadius="xs"
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Box, Grid } from '../../../packages/canon';
|
||||
|
||||
const FakeBox = () => (
|
||||
<Box
|
||||
borderRadius="xs"
|
||||
style={{
|
||||
background: '#eaf2fd',
|
||||
borderRadius: '4px',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { Box, Inline } from '../../../packages/canon';
|
||||
import { Box, Flex } from '../../../packages/canon';
|
||||
|
||||
const fakeBlockList = [
|
||||
{ width: 45, height: 60 },
|
||||
@@ -24,7 +24,6 @@ const FakeBox = ({
|
||||
height?: number;
|
||||
}) => (
|
||||
<Box
|
||||
borderRadius="xs"
|
||||
style={{
|
||||
background: '#eaf2fd',
|
||||
borderRadius: '4px',
|
||||
@@ -39,10 +38,10 @@ const FakeBox = ({
|
||||
|
||||
export const InlinePreview = () => {
|
||||
return (
|
||||
<Inline>
|
||||
<Flex>
|
||||
{fakeBlockList.map((block, index) => (
|
||||
<FakeBox key={index} width={block.width} height={block.height} />
|
||||
))}
|
||||
</Inline>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1510,6 +1510,7 @@ __metadata:
|
||||
eslint: ^8
|
||||
eslint-config-next: 14.2.23
|
||||
lightningcss: ^1.28.2
|
||||
motion: ^12.4.1
|
||||
next: 14.2.23
|
||||
react: ^18.0.2
|
||||
react-dom: ^18.0.2
|
||||
@@ -2584,6 +2585,28 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"framer-motion@npm:^12.4.1":
|
||||
version: 12.4.1
|
||||
resolution: "framer-motion@npm:12.4.1"
|
||||
dependencies:
|
||||
motion-dom: ^12.0.0
|
||||
motion-utils: ^12.0.0
|
||||
tslib: ^2.4.0
|
||||
peerDependencies:
|
||||
"@emotion/is-prop-valid": "*"
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
"@emotion/is-prop-valid":
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
checksum: 42679a16d9b99d5651b4cce157e56305b7341d94593d167647a478b3b934a6056693c8b274b5cd775557e6cc6b78e97981395c1a161c0467be922f58146b82ce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fs.realpath@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "fs.realpath@npm:1.0.0"
|
||||
@@ -4104,6 +4127,43 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"motion-dom@npm:^12.0.0":
|
||||
version: 12.0.0
|
||||
resolution: "motion-dom@npm:12.0.0"
|
||||
dependencies:
|
||||
motion-utils: ^12.0.0
|
||||
checksum: 58b6c7e678ac29b0d68b966e3e5879d2b3cc6b85ad4e3d141013e44666919669c42e9344508f91a4d35c3c6ddb71660a92a04414998046d10c2309bb630ebeb0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"motion-utils@npm:^12.0.0":
|
||||
version: 12.0.0
|
||||
resolution: "motion-utils@npm:12.0.0"
|
||||
checksum: 0b6ad4e10206e32a27fdd857df61ffdf929390eebda1d4832f6c85776677c7f8decdaf0f1eea10aa19e202f43a828bb32cade6e74a55ce7aec9d0c3fbd20dcf4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"motion@npm:^12.4.1":
|
||||
version: 12.4.1
|
||||
resolution: "motion@npm:12.4.1"
|
||||
dependencies:
|
||||
framer-motion: ^12.4.1
|
||||
tslib: ^2.4.0
|
||||
peerDependencies:
|
||||
"@emotion/is-prop-valid": "*"
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
"@emotion/is-prop-valid":
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
checksum: 4c6711da834de4e266dd64f6d81fcf1668193fed9afc011722b575d874412d2ac0206fbcb7675eccb4383fe5480e292d7d71652acfe6dab0b476515b72d9a601
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ms@npm:^2.1.1, ms@npm:^2.1.3":
|
||||
version: 2.1.3
|
||||
resolution: "ms@npm:2.1.3"
|
||||
|
||||
@@ -61,12 +61,12 @@ export class ImmediateEntityProvider implements EntityProvider {
|
||||
this.entityValidator = entitySchemaValidator();
|
||||
}
|
||||
|
||||
/** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */
|
||||
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */
|
||||
getProviderName() {
|
||||
return `ImmediateEntityProvider`;
|
||||
}
|
||||
|
||||
/** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */
|
||||
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */
|
||||
async connect(connection: EntityProviderConnection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
import {
|
||||
EntityProvider,
|
||||
EntityProviderConnection,
|
||||
} from '@backstage/plugin-catalog-backend';
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
/**
|
||||
@@ -41,12 +41,12 @@ import { Logger } from 'winston';
|
||||
export class LoadTestingEntityProvider implements EntityProvider {
|
||||
constructor(private readonly options: LoadTestingEntityProviderOptions) {}
|
||||
|
||||
/** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */
|
||||
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */
|
||||
getProviderName() {
|
||||
return `LoadTestingEntityProvider`;
|
||||
}
|
||||
|
||||
/** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */
|
||||
/** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */
|
||||
async connect(connection: EntityProviderConnection) {
|
||||
const delayStartup = this.options.delayStartup ?? 10_000;
|
||||
const logger = this.options.logger.child({
|
||||
|
||||
@@ -239,7 +239,8 @@ privateKey: |
|
||||
-----END RSA PRIVATE KEY-----
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Sensitive information, such as private keys, should not be hard coded.
|
||||
:::warning
|
||||
Sensitive information, such as private keys, should not be hard coded.
|
||||
:::
|
||||
|
||||
We recommend that this entire file should be a secret and stored as such in a secure storage solution like Vault, to ensure they are neither exposed nor misused. This example key part only shows the format on how to use the yaml | syntax to make sure that the key is valid.
|
||||
|
||||
@@ -7,20 +7,17 @@ description: Installing Kubernetes plugin into Backstage
|
||||
The Kubernetes feature is a plugin to Backstage, and it is exposed as a tab when
|
||||
viewing entities in the software catalog.
|
||||
|
||||
If you haven't setup Backstage already, read the
|
||||
[Getting Started](../../getting-started/index.md) guide.
|
||||
If you haven't set up Backstage already, read the [Getting Started](../../getting-started/index.md) guide.
|
||||
|
||||
## Adding the Kubernetes frontend plugin
|
||||
|
||||
The first step is to add the Kubernetes frontend plugin to your Backstage
|
||||
application.
|
||||
The first step is to add the Kubernetes frontend plugin to your Backstage application.
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/app add @backstage/plugin-kubernetes
|
||||
```
|
||||
|
||||
Once the package has been installed, you need to import the plugin in your app
|
||||
by adding the "Kubernetes" tab to the respective catalog pages.
|
||||
Once the package has been installed, you need to import the plugin in your app by adding the "Kubernetes" tab to the respective catalog pages.
|
||||
|
||||
```tsx title="packages/app/src/components/catalog/EntityPage.tsx"
|
||||
/* highlight-add-next-line */
|
||||
@@ -40,73 +37,17 @@ const serviceEntityPage = (
|
||||
);
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
:::note Note
|
||||
|
||||
- The optional `refreshIntervalMs` property on the `EntityKubernetesContent` defines the interval in which the content automatically refreshes, if not set this will default to 10 seconds.
|
||||
The optional `refreshIntervalMs` property on the `EntityKubernetesContent` defines the interval in which the content automatically refreshes, if not set this will default to 10 seconds.
|
||||
|
||||
That's it! But now, we need the Kubernetes Backend plugin for the frontend to
|
||||
work.
|
||||
:::
|
||||
|
||||
That's it! But now, we need the Kubernetes Backend plugin for the frontend to work.
|
||||
|
||||
## Adding Kubernetes Backend plugin
|
||||
|
||||
Navigate to `packages/backend` of your Backstage app, and install the
|
||||
`@backstage/plugin-kubernetes-backend` package.
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend
|
||||
```
|
||||
|
||||
Create a file called `kubernetes.ts` inside `packages/backend/src/plugins/` and
|
||||
add the following:
|
||||
|
||||
```ts title="packages/backend/src/plugins/kubernetes.ts"
|
||||
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
catalogApi,
|
||||
discovery: env.discovery,
|
||||
permissions: env.permissions,
|
||||
}).build();
|
||||
return router;
|
||||
}
|
||||
```
|
||||
|
||||
And import the plugin to `packages/backend/src/index.ts`. There are three lines
|
||||
of code you'll need to add, and they should be added near similar code in your
|
||||
existing Backstage backend.
|
||||
|
||||
```typescript title="packages/backend/src/index.ts"
|
||||
// ..
|
||||
/* highlight-add-next-line */
|
||||
import kubernetes from './plugins/kubernetes';
|
||||
|
||||
async function main() {
|
||||
// ...
|
||||
/* highlight-add-next-line */
|
||||
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
|
||||
// ...
|
||||
/* highlight-add-next-line */
|
||||
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
|
||||
}
|
||||
```
|
||||
|
||||
That's it! The Kubernetes frontend and backend have now been added to your
|
||||
Backstage app.
|
||||
|
||||
### New Backend System
|
||||
|
||||
To get the Kubernetes plugin install using the New Backend System you will need to do the following:
|
||||
|
||||
Run this command to add the package:
|
||||
First, we need to add the backend package:
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend
|
||||
@@ -126,6 +67,9 @@ backend.add(import('@backstage/plugin-kubernetes-backend'));
|
||||
backend.start();
|
||||
```
|
||||
|
||||
That's it! The Kubernetes frontend and backend have now been added to your
|
||||
Backstage app.
|
||||
|
||||
### Custom cluster discovery
|
||||
|
||||
If either existing
|
||||
@@ -133,22 +77,18 @@ If either existing
|
||||
don't work for your use-case, it is possible to implement a custom
|
||||
[KubernetesClustersSupplier](https://backstage.io/docs/reference/plugin-kubernetes-backend.kubernetesclusterssupplier).
|
||||
|
||||
Change the following in `packages/backend/src/plugins/kubernetes.ts`:
|
||||
Here's a very simplified example:
|
||||
|
||||
```ts title="packages/backend/src/plugins/kubernetes.ts"
|
||||
import {
|
||||
/* highlight-add-next-line */
|
||||
ClusterDetails,
|
||||
KubernetesBuilder,
|
||||
/* highlight-add-next-line */
|
||||
KubernetesClustersSupplier,
|
||||
} from '@backstage/plugin-kubernetes-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
/* highlight-add-next-line */
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
import { createBackend } from '@backstage/backend-defaults';
|
||||
import { createBackendModule } from '@backstage/backend-plugin-api';
|
||||
import { Duration } from 'luxon';
|
||||
import {
|
||||
ClusterDetails,
|
||||
KubernetesClustersSupplier,
|
||||
kubernetesClusterSupplierExtensionPoint,
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
|
||||
/* highlight-add-start */
|
||||
export class CustomClustersSupplier implements KubernetesClustersSupplier {
|
||||
constructor(private clusterDetails: ClusterDetails[] = []) {}
|
||||
|
||||
@@ -170,43 +110,6 @@ export class CustomClustersSupplier implements KubernetesClustersSupplier {
|
||||
return this.clusterDetails;
|
||||
}
|
||||
}
|
||||
/* highlight-add-end */
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
|
||||
/* highlight-remove-next-line */
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
/* highlight-add-next-line */
|
||||
const builder = await KubernetesBuilder.createBuilder({
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
/* highlight-remove-next-line */
|
||||
}).build();
|
||||
/* highlight-add-start */
|
||||
});
|
||||
builder.setClusterSupplier(
|
||||
CustomClustersSupplier.create(Duration.fromObject({ minutes: 60 })),
|
||||
);
|
||||
const { router } = await builder.build();
|
||||
/* highlight-add-end */
|
||||
|
||||
// ..
|
||||
return router;
|
||||
}
|
||||
```
|
||||
|
||||
### New Backend System Custom cluster discovery
|
||||
|
||||
To use Custom cluster discovery with the New Backend System you'll need to create a module and add it to your backend. Here's a very simplified example:
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
import { createBackend } from '@backstage/backend-defaults';
|
||||
import { createBackendModule } from '@backstage/backend-plugin-api';
|
||||
import { Duration } from 'luxon';
|
||||
import { kubernetesClusterSupplierExtensionPoint } from '@backstage/plugin-kubernetes-node';
|
||||
import { CustomClustersSupplier } from './path/to/class';
|
||||
|
||||
const backend = createBackend();
|
||||
|
||||
@@ -236,7 +139,7 @@ backend.start();
|
||||
|
||||
:::note Note
|
||||
|
||||
This example assumes the `CustomClustersSupplier` class is the same from the [previous example](#custom-cluster-discovery)
|
||||
This example uses items from the `@backstage/plugin-kubernetes-node` and `luxon` packages, you'll need to add those for this example to work as is.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Initial support for pagination of the `CatalogIndexPage` was added in v1.21.0 of
|
||||
|
||||
## Initially Selected Filter
|
||||
|
||||
By default the initially selected filter defaults to Owned. If you are still building up your catalog this may show an empty list to start. If you would prefer this to show All as the default, here's how you can make that change:
|
||||
By default, the initially selected filter defaults to Owned. If you are still building up your catalog this may show an empty list to start. If you would prefer this to show All as the default, here's how you can make that change:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
<Route
|
||||
@@ -30,7 +30,7 @@ Possible options are: owned, starred, or all
|
||||
|
||||
## Initially Selected Kind
|
||||
|
||||
By default the initially selected Kind when viewing the Catalog is Component, but you may have reasons that you want this to be different. Let's say at your Organization they would like it to always default to Domain, here's how you would do that:
|
||||
By default, the initially selected Kind when viewing the Catalog is Component, but you may have reasons that you want this to be different. Let's say at your Organization they would like it to always default to Domain, here's how you would do that:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
<Route path="/catalog" element={<CatalogIndexPage initialKind="domain" />} />
|
||||
@@ -70,9 +70,7 @@ The columns you see in the `CatalogIndexPage` were selected to be a good startin
|
||||
Suppose we want to add a new User Email column to the `User` kind in the Catalog. We can do this by overriding the `columns` that we pass into the `CatalogIndexPage` component in our `App.tsx`. First, we need to match the entity kind that we want to override, and then define the columns to show:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
{
|
||||
/* highlight-add-start */
|
||||
}
|
||||
{/* prettier-ignore */ /* highlight-add-start */}
|
||||
const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => {
|
||||
if (entityListContext.filters.kind?.value === 'user') {
|
||||
return [
|
||||
@@ -84,9 +82,7 @@ const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => {
|
||||
|
||||
return CatalogTable.defaultColumnsFunc(entityListContext);
|
||||
};
|
||||
{
|
||||
/* highlight-add-end */
|
||||
}
|
||||
{/* prettier-ignore */ /* highlight-add-end */}
|
||||
```
|
||||
|
||||
Then, we can implement the `createUserEmailColumn` function and add it to the list of columns. `field` is used to access the data from the entity, while `render` lets us customize how we display the data:
|
||||
@@ -107,7 +103,6 @@ const createUserEmailColumn = (): TableColumn<CatalogTableRow> => ({
|
||||
|
||||
const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => {
|
||||
if (entityListContext.filters.kind?.value === 'user') {
|
||||
return [
|
||||
return [
|
||||
// Render existing columns
|
||||
...CatalogTable.defaultColumnsFunc(entityListContext),
|
||||
@@ -391,13 +386,9 @@ export const EntitySecurityTierPicker = () => {
|
||||
Now we can add the component to `CatalogIndexPage`:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
{
|
||||
/* highlight-add-start */
|
||||
}
|
||||
{/* prettier-ignore */ /* highlight-add-start */}
|
||||
import { DefaultFilters } from '@backstage/plugin-catalog-react';
|
||||
{
|
||||
/* highlight-add-end */
|
||||
}
|
||||
{/* prettier-ignore */ /* highlight-add-end */}
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
|
||||
@@ -495,8 +495,8 @@ want to have an isomorphic package that houses these types. Within the Backstage
|
||||
main repo the package naming pattern of `<plugin>-common` is used for isomorphic
|
||||
packages, and you may choose to adopt this pattern as well.
|
||||
|
||||
You can generate an isomorphic plugin package by running:`yarn new --select plugin-common`
|
||||
or you can run `yarn new` and then select "plugin-common" from the list of options
|
||||
You can generate an isomorphic plugin package by running: `yarn new` and then
|
||||
select "plugin-common" from the list of options
|
||||
|
||||
There's at this point no existing templates for generating isomorphic plugins
|
||||
using the `@backstage/cli`. Perhaps the simplest way to get started right now is
|
||||
|
||||
@@ -495,8 +495,8 @@ want to have an isomorphic package that houses these types. Within the Backstage
|
||||
main repo the package naming pattern of `<plugin>-common` is used for isomorphic
|
||||
packages, and you may choose to adopt this pattern as well.
|
||||
|
||||
You can generate an isomorphic plugin package by running:`yarn new --select plugin-common`
|
||||
or you can run `yarn new` and then select "plugin-common" from the list of options
|
||||
You can generate an isomorphic plugin package by running: `yarn new` and then
|
||||
selecting "plugin-common" from the list of options
|
||||
|
||||
There's at this point no existing templates for generating isomorphic plugins
|
||||
using the `@backstage/cli`. Perhaps the simplest way to get started right now is
|
||||
@@ -520,7 +520,7 @@ validate entities of our new kind. Just like with the definition package, you
|
||||
can find inspiration in for example the existing
|
||||
[ScaffolderEntitiesProcessor](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend-module-scaffolder-entity-model/src/processor/ScaffolderEntitiesProcessor.ts).
|
||||
|
||||
The custom processor should be created as a separate module for the catalog plugin. For information on how to set that up, see the [plugin docs](../../plugins/backend-plugin.md#creating-a-backend-plugin). Use `yarn new --select backend-module` instead to create a module. For our case, the module ID will be `foobar` and the plugin ID will be `catalog`.
|
||||
The custom processor should be created as a separate module for the catalog plugin. For information on how to set that up, see the [plugin docs](../../plugins/backend-plugin.md#creating-a-backend-plugin). Use `yarn new` and select `backend-module` instead to create a module. For our case, the module ID will be `foobar` and the plugin ID will be `catalog`.
|
||||
|
||||
We also provide a high-level example of what a catalog process for a custom
|
||||
entity might look like:
|
||||
|
||||
@@ -72,7 +72,7 @@ putting all extensions like this in a backend module package of their own in the
|
||||
`plugins` folder of your Backstage repo:
|
||||
|
||||
```sh
|
||||
yarn new --select backend-module --option id=catalog
|
||||
yarn new --select backend-module --option pluginId=catalog
|
||||
```
|
||||
|
||||
The class will have this basic structure:
|
||||
@@ -650,7 +650,7 @@ putting all extensions like this in a backend module package of their own in the
|
||||
`plugins` folder of your Backstage repo:
|
||||
|
||||
```sh
|
||||
yarn new --select backend-module --option id=catalog
|
||||
yarn new --select backend-module --option pluginId=catalog
|
||||
```
|
||||
|
||||
The class will have this basic structure:
|
||||
|
||||
@@ -189,7 +189,7 @@ parameters:
|
||||
title: Select features
|
||||
type: array
|
||||
items:
|
||||
type: boolean
|
||||
type: string
|
||||
enum:
|
||||
- 'Enable scraping'
|
||||
- 'Enable HPA'
|
||||
|
||||
@@ -0,0 +1,502 @@
|
||||
---
|
||||
id: template-extensions
|
||||
title: Template Extensions
|
||||
description: Template extensions system
|
||||
---
|
||||
|
||||
Backstage templating is powered by [Nunjucks][]. The basics:
|
||||
|
||||
# Template Filters
|
||||
|
||||
The [filter][] is a critical mechanism for the rendering of Nunjucks templates,
|
||||
providing a means of transforming values in a familiar [piped][] fashion.
|
||||
Template filters are functions that help you transform data, extract specific
|
||||
information, and perform various operations in Scaffolder Templates.
|
||||
|
||||
## Built-in
|
||||
|
||||
Backstage provides out of the box the following set of "built-in" template
|
||||
filters (to create your own custom filters, look to the section [Custom Filter](#custom-filter) hereafter):
|
||||
|
||||
### parseRepoUrl
|
||||
|
||||
The `parseRepoUrl` filter parses a repository URL into its constituent parts:
|
||||
`owner`, repository name (`repo`), etc.
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Repo URL
|
||||
action: debug:log
|
||||
input:
|
||||
message: ${{ parameters.repoUrl | parseRepoUrl }}
|
||||
```
|
||||
|
||||
- **Input**: `github.com?repo=backstage&owner=backstage`
|
||||
- **Output**: "RepoSpec" (see [parseRepoUrl][])
|
||||
|
||||
### parseEntityRef
|
||||
|
||||
The `parseEntityRef` filter allows you to extract different parts of
|
||||
an entity reference, such as the `kind`, `namespace`, and `name`.
|
||||
|
||||
**Usage example**
|
||||
|
||||
1. Without context
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
message: ${{ parameters.owner | parseEntityRef }}
|
||||
```
|
||||
|
||||
- **Input**: `group:techdocs`
|
||||
- **Output**: [CompoundEntityRef][]
|
||||
|
||||
1. With context
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
message: ${{ parameters.owner | parseEntityRef({ defaultKind:"group", defaultNamespace:"another-namespace" }) }}
|
||||
```
|
||||
|
||||
- **Input**: `techdocs`
|
||||
- **Output**: [CompoundEntityRef][]
|
||||
|
||||
### pick
|
||||
|
||||
The `pick` filter allows you to select a specific property (e.g. `kind`, `namespace`, `name`) from an object.
|
||||
|
||||
**Usage Example**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Pick
|
||||
action: debug:log
|
||||
input:
|
||||
message: ${{ parameters.owner | parseEntityRef | pick('name') }}
|
||||
```
|
||||
|
||||
- **Input**: `{ kind: 'Group', namespace: 'default', name: 'techdocs' }`
|
||||
- **Output**: `techdocs`
|
||||
|
||||
### projectSlug
|
||||
|
||||
The `projectSlug` filter generates a project slug from a repository URL.
|
||||
|
||||
**Usage Example**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Project Slug
|
||||
action: debug:log
|
||||
input:
|
||||
message: ${{ parameters.repoUrl | projectSlug }}
|
||||
```
|
||||
|
||||
- **Input**: `github.com?repo=backstage&owner=backstage`
|
||||
- **Output**: `backstage/backstage`
|
||||
|
||||
# Template Globals
|
||||
|
||||
In addition to its powerful filtering functionality, the Nunjucks engine allows
|
||||
access from the template expression context to specified globally-accessible
|
||||
references. Backstage propagates this capability via the scaffolder backend
|
||||
plugin, which we shall soon see in action.
|
||||
|
||||
# Customizing the templating environment
|
||||
|
||||
Custom plugins make it possible to install your own template extensions, which
|
||||
may be any combination of filters, global functions and global values. With the
|
||||
new backend you would use a scaffolder plugin module for this; later we will
|
||||
demonstrate the analogous approach with the old backend.
|
||||
|
||||
## Streamlining Template Extension Module Creation with the Backstage CLI
|
||||
|
||||
The creation of a "template environment customization" module in Backstage can
|
||||
be accelerated using the Backstage CLI.
|
||||
|
||||
Start by using the `yarn backstage-cli new` command to generate a scaffolder module. This command sets up the necessary boilerplate code, providing a smooth start:
|
||||
|
||||
```
|
||||
$ yarn backstage-cli new
|
||||
? What do you want to create?
|
||||
> backend-module - A new backend module that extends an existing backend plugin with additional features
|
||||
backend-plugin - A new backend plugin
|
||||
plugin - A new frontend plugin
|
||||
node-library - A new node-library package, exporting shared functionality for backend plugins and modules
|
||||
plugin-common - A new isomorphic common plugin package
|
||||
plugin-node - A new Node.js library plugin package
|
||||
plugin-react - A new web library plugin package
|
||||
scaffolder-module - An module exporting custom actions for @backstage/plugin-scaffolder-backend
|
||||
```
|
||||
|
||||
When prompted, select the option to generate a backend module.
|
||||
Since we want to extend the Scaffolder backend, enter `scaffolder` when prompted for the plugin to extend.
|
||||
Next, enter a name for your module (relative to the generated `scaffolder-backend-module-` prefix),
|
||||
and the CLI will generate the required files and directory structure.
|
||||
|
||||
## Writing your Module
|
||||
|
||||
Once the CLI has generated the essential structure for your new scaffolder
|
||||
module, it's time to implement our template extensions. Here we'll demonstrate
|
||||
how to create each of the supported extension types.
|
||||
|
||||
`src/module.ts` is where the magic happens. First we prepare to utilize the
|
||||
associated (_**alpha** phase_) API extension point by adding:
|
||||
|
||||
```ts
|
||||
import { scaffolderTemplatingExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
```
|
||||
|
||||
Considering the generated code, you may observe that everything rests on the
|
||||
`createBackendModule` call, which after providing some minimal metadata to
|
||||
establish context, specifies a `register` callback whose sole responsibility
|
||||
here is to call, in turn, `registerInit` against the
|
||||
`BackendModuleRegistrationPoints` argument it receives. Modify this call to
|
||||
make the `scaffolderTemplatingExtensionPoint` available to the specified `init`
|
||||
function:
|
||||
|
||||
```ts
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {
|
||||
...,
|
||||
templating: scaffolderTemplatingExtensionPoint,
|
||||
},
|
||||
async init({
|
||||
...,
|
||||
templating
|
||||
}) {
|
||||
...
|
||||
};
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
Now we're ready to extend the scaffolder templating engine. For our purposes
|
||||
here we'll drop everything in `module.ts`; use your own judgment as to the
|
||||
organization of your real-world plugin modules.
|
||||
|
||||
### Custom Filter
|
||||
|
||||
In this contrived example we add a filter to test whether the incoming string
|
||||
value contains (at least) a specified number of occurrences of a given
|
||||
substring. We can easily define this by adding code to our `init` callback:
|
||||
|
||||
```ts
|
||||
async init({
|
||||
...,
|
||||
templating,
|
||||
}) {
|
||||
...
|
||||
templating.addTemplateFilters({
|
||||
containsOccurrences: (arg: string, substring: string, times: number) => {
|
||||
let pos = 0;
|
||||
let count = 0;
|
||||
while (pos < arg.length) {
|
||||
pos = arg.indexOf(substring, pos);
|
||||
if (pos < 0) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count === times;
|
||||
},
|
||||
});
|
||||
},
|
||||
```
|
||||
|
||||
This demonstrates the bare minimum: a TypeScript `Record` of named template
|
||||
filter implementations to register. However, by adopting an alternate structure
|
||||
we can document our filter with additional metadata; to utilize this capability
|
||||
we begin by adding a new import:
|
||||
|
||||
```ts
|
||||
import { createTemplateFilter } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
```
|
||||
|
||||
Then, update your `init` implementation to specify an array rather than an
|
||||
object/record:
|
||||
|
||||
```ts
|
||||
async init({
|
||||
...,
|
||||
templating,
|
||||
}) {
|
||||
...
|
||||
templating.addTemplateFilters([
|
||||
createTemplateFilter({
|
||||
id: 'containsOccurrences',
|
||||
description: 'determine whether filter input contains a substring N times',
|
||||
filter: (arg: string, substring: string, times: number) => {
|
||||
let pos = 0;
|
||||
let count = 0;
|
||||
while (pos < arg.length) {
|
||||
pos = arg.indexOf(substring, pos);
|
||||
if (pos < 0) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count === times;
|
||||
},
|
||||
}),
|
||||
]);
|
||||
},
|
||||
```
|
||||
|
||||
With this we have added a `description` to our filter, which helps a template
|
||||
author to understand the filter's purpose.
|
||||
|
||||
#### Schema
|
||||
|
||||
To enhance our filter documentation further, we will specify its `schema`
|
||||
using a callback against the [Zod][] schema declaration library:
|
||||
|
||||
```ts
|
||||
createTemplateFilter({
|
||||
id: 'containsOccurrences',
|
||||
description: 'determine whether filter input contains a substring N times',
|
||||
schema: z =>
|
||||
z.function(
|
||||
z.tuple([
|
||||
z.string().describe('input'),
|
||||
z.string().describe('substring whose occurrences to find'),
|
||||
z.number().describe('number of occurrences to check for'),
|
||||
]),
|
||||
z.boolean(),
|
||||
),
|
||||
...,
|
||||
}),
|
||||
```
|
||||
|
||||
Because a filter is, in fact, a function, its schema is defined by generating a
|
||||
[Zod function schema][zod-fn] against the parameter supplied to our schema
|
||||
callback. A filter function is required to have at least one argument; in this
|
||||
example, we have two additional arguments. But what if we modify our filter's
|
||||
implementation function to make `times` optional? Code:
|
||||
|
||||
```ts
|
||||
createTemplateFilter({
|
||||
id: 'containsOccurrences',
|
||||
...,
|
||||
filter: (arg: string, substring: string, times?: number) => {
|
||||
if (times === undefined) {
|
||||
// note that, in real life, simply calling this function directly with Nunjucks would suffice rather than implementing a filter:
|
||||
return arg.includes(substring);
|
||||
}
|
||||
// original implementation follows
|
||||
...
|
||||
},
|
||||
}),
|
||||
```
|
||||
|
||||
In this case we should modify our `schema`:
|
||||
|
||||
```ts
|
||||
createTemplateFilter({
|
||||
...,
|
||||
schema: z =>
|
||||
z.function(
|
||||
z.tuple([
|
||||
z.string().describe('input'),
|
||||
z.string().describe('substring whose occurrences to find'),
|
||||
z
|
||||
.number()
|
||||
.describe('number of occurrences to check for')
|
||||
.optional(),
|
||||
]),
|
||||
z.boolean(),
|
||||
),
|
||||
...,
|
||||
}),
|
||||
```
|
||||
|
||||
#### Filter Example Documentation
|
||||
|
||||
Our filter documentation may benefit from examples which we specify thus:
|
||||
|
||||
```ts
|
||||
createTemplateFilter({
|
||||
...,
|
||||
examples: [
|
||||
{
|
||||
description: 'Basic Usage',
|
||||
example: `\
|
||||
- name: Contains Occurrences
|
||||
action: debug:log
|
||||
input:
|
||||
message: \${{ parameters.projectName | containsOccurrences('-', 2) }}
|
||||
`,
|
||||
notes: `\
|
||||
- **Input**: \`foo-bar-baz\`
|
||||
- **Output**: \`true\`
|
||||
`,
|
||||
},
|
||||
{
|
||||
description: 'Omitting Optional Parameter',
|
||||
example: `\
|
||||
- name: Contains baz
|
||||
action: debug:log
|
||||
input:
|
||||
message: \${{ parameters.projectName | containsOccurrences('baz') | dump }}
|
||||
`,
|
||||
notes: `\
|
||||
- **Input**: \`foo-bar\`
|
||||
- **Output**: \`false\`
|
||||
`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
```
|
||||
|
||||
### Custom Global Function
|
||||
|
||||
In case your template needs access to a value generated from a function not
|
||||
appropriately modeled as a filter, Nunjucks supports the direct invocation of
|
||||
[global functions][global-fn]. We might, for example, add to `init`:
|
||||
|
||||
```ts
|
||||
async init({
|
||||
...,
|
||||
templating,
|
||||
}) {
|
||||
...
|
||||
templating.addTemplateGlobals({
|
||||
now: () => new Date().toISOString(),
|
||||
});
|
||||
},
|
||||
```
|
||||
|
||||
Here we have implemented a simple mechanism to obtain a timestamp (note that
|
||||
because we can only pass JSON-compatible--or `undefined`--values we have chosen
|
||||
to model a date/time as an ISO string) using a globally available function.
|
||||
|
||||
Again we have the option to make our global function self-documenting. Import:
|
||||
|
||||
```ts
|
||||
import {
|
||||
...,
|
||||
createTemplateGlobalFunction,
|
||||
} from '@backstage/plugin-scaffolder-node/alpha';
|
||||
```
|
||||
|
||||
Then modify:
|
||||
|
||||
```ts
|
||||
...
|
||||
templating.addTemplateGlobals([
|
||||
createTemplateGlobalFunction({
|
||||
id: 'now',
|
||||
description:
|
||||
'obtain an ISO representation of the current date and time',
|
||||
fn: () => new Date().toISOString(),
|
||||
}),
|
||||
]);
|
||||
```
|
||||
|
||||
#### Schema
|
||||
|
||||
Declaring a global function schema is quite like the schema declaration for a
|
||||
template filter:
|
||||
|
||||
```ts
|
||||
createTemplateGlobal({
|
||||
...,
|
||||
schema: z => z.function().args().returns(z.string()),
|
||||
...,
|
||||
}),
|
||||
```
|
||||
|
||||
#### Template Global Function Example Documentation
|
||||
|
||||
Again, this works in the same way as filter examples:
|
||||
|
||||
```ts
|
||||
createTemplateGlobal({
|
||||
...,
|
||||
examples: [
|
||||
{
|
||||
description: 'Obtain the current date/time',
|
||||
example: `\
|
||||
- name: Log Timestamp
|
||||
action: debug:log
|
||||
input:
|
||||
message: Current date/time: \${{ now() }}
|
||||
`,
|
||||
// optional `notes` omitted from this example
|
||||
},
|
||||
],
|
||||
...,
|
||||
}),
|
||||
|
||||
```
|
||||
|
||||
### Custom Global Value
|
||||
|
||||
Alternatively, your template may need access to a simple JSON value, which can
|
||||
be registered in this manner:
|
||||
|
||||
```ts
|
||||
async init({
|
||||
...,
|
||||
templating,
|
||||
}) {
|
||||
...
|
||||
templating.addTemplateGlobals({
|
||||
...,
|
||||
preferredMetasyntacticIdentifier: 'foo',
|
||||
});
|
||||
},
|
||||
```
|
||||
|
||||
Or the documenting form:
|
||||
|
||||
```ts
|
||||
async init({
|
||||
...,
|
||||
templating,
|
||||
}) {
|
||||
...
|
||||
templating.addTemplateGlobals([
|
||||
...,
|
||||
createTemplateGlobalValue({
|
||||
id: 'preferredMetasyntacticVariable',
|
||||
value: 'foo',
|
||||
description:
|
||||
'This description is as contrived as the global value it documents',
|
||||
}),
|
||||
]);
|
||||
},
|
||||
```
|
||||
|
||||
## Register Template Extensions with the Legacy Backend System
|
||||
|
||||
Users of the original Backstage backend can register template extensions by
|
||||
specifying options to the scaffolder backend plugin's `createRouter` function
|
||||
(customarily called in `packages/backend/src/plugins/scaffolder.ts`):
|
||||
|
||||
- `additionalTemplateFilters` - either of:
|
||||
- object mapping filter name to implementation function, or
|
||||
- array of documented template filters as returned by the
|
||||
utility function `createTemplateFilter`
|
||||
- `additionalTemplateGlobals` - either of:
|
||||
- object mapping global name to value or function, or
|
||||
- array of documented global functions and values as returned by the utility
|
||||
functions `createTemplateGlobalFunction` and `createTemplateGlobalValue`
|
||||
|
||||
[nunjucks]: https://mozilla.github.io/nunjucks
|
||||
[filter]: https://mozilla.github.io/nunjucks/templating.html#filters
|
||||
[global-fn]: https://mozilla.github.io/nunjucks/templating.html#global-functions
|
||||
[parseRepoUrl]: https://backstage.io/docs/reference/plugin-scaffolder-node.parserepourl
|
||||
[CompoundEntityRef]: https://backstage.io/docs/reference/catalog-model.compoundentityref
|
||||
[Zod]: https://zod.dev/
|
||||
[zod-fn]: https://zod.dev/?id=functions
|
||||
[piped]: https://en.wikipedia.org/wiki/Pipeline_(Unix)#Pipelines_in_command_line_interfaces
|
||||
@@ -631,7 +631,7 @@ output:
|
||||
|
||||
## The templating syntax
|
||||
|
||||
You might have noticed variables wrapped in `${{ }}` in the examples. These are
|
||||
You might have noticed expressions wrapped in `${{ }}` in the examples. These are
|
||||
template strings for linking and gluing the different parts of the template
|
||||
together. All the form inputs from the `parameters` section will be available by
|
||||
using this template syntax (for example, `${{ parameters.firstName }}` inserts
|
||||
@@ -704,219 +704,16 @@ You can read more about all the `inputs` and `outputs` defined in the actions in
|
||||
code part of the `JSONSchema`, or you can read more about our
|
||||
[built in actions](./builtin-actions.md).
|
||||
|
||||
## Built in Filters
|
||||
### More about expressions
|
||||
|
||||
Template filters are functions that help you transform data, extract specific information,
|
||||
and perform various operations in Scaffolder Templates.
|
||||
The `${{ }}` constructs in your template are evaluated using the
|
||||
powerful [Nunjucks templating engine](https://mozilla.github.io/nunjucks/).
|
||||
To learn more about basic Nunjucks templating please see
|
||||
[templating documentation](https://mozilla.github.io/nunjucks/templating.html).
|
||||
|
||||
This section introduces the built-in filters provided by Backstage and offers examples of
|
||||
how to use them in the Scaffolder templates. It's important to mention that Backstage also leverages the
|
||||
native filters from the Nunjucks library. For a complete list of these native filters and their usage,
|
||||
refer to the [Nunjucks documentation](https://mozilla.github.io/nunjucks/templating.html#builtin-filters).
|
||||
|
||||
To create your own custom filters, look to the section [Custom Filters and Globals](#custom-filters-and-globals) hereafter.
|
||||
|
||||
### parseRepoUrl
|
||||
|
||||
The `parseRepoUrl` filter parse a repository URL into
|
||||
its components, such as `owner`, repository `name`, and more.
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Repo URL
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.repoUrl | parseRepoUrl }}
|
||||
```
|
||||
|
||||
- **Input**: `github.com?repo=backstage&org=backstage`
|
||||
- **Output**: [RepoSpec](https://github.com/backstage/backstage/blob/v1.17.2/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts#L39)
|
||||
|
||||
### parseEntityRef
|
||||
|
||||
The `parseEntityRef` filter allows you to extract different parts of
|
||||
an entity reference, such as the `kind`, `namespace`, and `name`.
|
||||
|
||||
**Usage example**
|
||||
|
||||
1. Without context
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.owner | parseEntityRef }}
|
||||
```
|
||||
|
||||
- **Input**: `group:techdocs`
|
||||
- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23)
|
||||
|
||||
2. With context
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Parse Entity Reference
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.owner | parseEntityRef({ defaultKind:"group", defaultNamespace:"another-namespace" }) }}
|
||||
```
|
||||
|
||||
- **Input**: `techdocs`
|
||||
- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23)
|
||||
|
||||
### pick
|
||||
|
||||
This `pick` filter allows you to select specific properties (`kind`, `namespace`, `name`) from an object.
|
||||
|
||||
**Usage Example**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Pick
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.owner | parseEntityRef | pick('name') }}
|
||||
```
|
||||
|
||||
- **Input**: `{ kind: 'Group', namespace: 'default', name: 'techdocs' }`
|
||||
- **Output**: `techdocs`
|
||||
|
||||
### projectSlug
|
||||
|
||||
The `projectSlug` filter generates a project slug from a repository URL
|
||||
|
||||
**Usage Example**
|
||||
|
||||
```yaml
|
||||
- id: log
|
||||
name: Project Slug
|
||||
action: debug:log
|
||||
input:
|
||||
extra: ${{ parameters.repoUrl | projectSlug }}
|
||||
```
|
||||
|
||||
- **Input**: `github.com?repo=backstage&org=backstage`
|
||||
- **Output**: `backstage/backstage`
|
||||
|
||||
## Custom Filters and Globals
|
||||
|
||||
You may wish to extend the filters and globals with your own custom ones. For example `${{ myGlobal | myFilter | myOtherFilter }}` or `${{ myFunctionGlobal(1,2) | myFilter }}`.
|
||||
This can be achieved using the `additionalTemplateFilters` and `additionalTemplateGlobals` properties respectively.
|
||||
|
||||
These properties accept a `Record`
|
||||
|
||||
```ts title="plugins/scaffolder-backend/src/service/router.ts"
|
||||
additionalTemplateFilters?: Record<string, TemplateFilter>;
|
||||
additionalTemplateGlobals?: Record<string, TemplateGlobal>;
|
||||
```
|
||||
|
||||
where the first parameter is the identifier of the filter or global and the second is a `TemplateFilter` or a `TemplateGlobal` respectively.
|
||||
A `TemplateFilter` is a function which will be called using the previous `JsonValue` objects and may return a `JsonValue` object.
|
||||
A `TemplateGlobal` can either be a function which will be called using the passed `JsonValue` objects and may return a `JsonValue` object or it can be a `JsonValue` object itself.
|
||||
|
||||
```ts title="plugins/scaffolder-node/src/types.ts"
|
||||
export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined;
|
||||
|
||||
export type TemplateGlobal =
|
||||
| ((...args: JsonValue[]) => JsonValue | undefined)
|
||||
| JsonValue;
|
||||
```
|
||||
|
||||
**Usage Example**
|
||||
|
||||
Given you want to have the following filters and globals available in you template:
|
||||
|
||||
```yaml
|
||||
apiVersion: scaffolder.backstage.io/v1beta3
|
||||
kind: Template
|
||||
metadata:
|
||||
name: test
|
||||
title: Test
|
||||
spec:
|
||||
owner: user:guest
|
||||
type: service
|
||||
|
||||
steps:
|
||||
- id: debug1
|
||||
name: debug1
|
||||
action: debug:log
|
||||
input:
|
||||
message: ${{ myGlobal | myFilter | myOtherFilter }}
|
||||
|
||||
- id: debug2
|
||||
name: debug2
|
||||
action: debug:log
|
||||
input:
|
||||
message: ${{ myFunctionGlobal(1,2) | myFilter }}
|
||||
```
|
||||
|
||||
You will have to create a new [`BackendModule`](../../backend-system/architecture/06-modules.md) using the `scaffolderTemplatingExtensionPoint`.
|
||||
|
||||
Here is a very simplified example of how to do that:
|
||||
|
||||
```ts title="packages/backend-next/src/index.ts"
|
||||
/* highlight-add-start */
|
||||
import { scaffolderTemplatingExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha';
|
||||
import { createBackendModule } from '@backstage/backend-plugin-api';
|
||||
/* highlight-add-end */
|
||||
|
||||
/* highlight-add-start */
|
||||
const scaffolderModuleCustomFilters = createBackendModule({
|
||||
pluginId: 'scaffolder', // name of the plugin that the module is targeting
|
||||
moduleId: 'custom-filters',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
scaffolder: scaffolderTemplatingExtensionPoint,
|
||||
// ... and other dependencies as needed
|
||||
},
|
||||
async init({ scaffolder /* ..., other dependencies */ }) {
|
||||
scaffolder.addTemplateGlobals({
|
||||
myGlobal: () => 'myGlobal',
|
||||
myFunctionGlobal: (...args: JsonValue[]) => args[0] + args[1],
|
||||
});
|
||||
scaffolder.addTemplateFilters({
|
||||
myFilter: () => 'the value is this now',
|
||||
myOtherFilter: (...args: JsonValue[]) => args.join(''),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
/* highlight-add-end */
|
||||
|
||||
const backend = createBackend();
|
||||
backend.add(import('@backstage/plugin-scaffolder-backend'));
|
||||
/* highlight-add-next-line */
|
||||
backend.add(scaffolderModuleCustomFilters);
|
||||
```
|
||||
|
||||
If you still use the legacy backend system, then you will use the `createRouter()` function of the `Scaffolder plugin`
|
||||
|
||||
```ts title="packages/backend/src/plugins/scaffolder.ts"
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
config,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
...
|
||||
return await createRouter({
|
||||
logger,
|
||||
config,
|
||||
|
||||
additionalTemplateFilters: {
|
||||
<YOUR_FILTERS>
|
||||
},
|
||||
additionalTemplateGlobals: {
|
||||
<YOUR_GLOBALS>
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Note that additional template global functions are currently not supported in `fetch:template` (see #25445).
|
||||
Information about Backstage's built-in Nunjucks extensions, as well as how to
|
||||
create your own customizations, may be found at
|
||||
[Template Extensions](./template-extensions.md).
|
||||
|
||||
## Template Editor
|
||||
|
||||
|
||||
@@ -337,3 +337,21 @@ const routableExtension = createExtension({
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Multiple attachment points
|
||||
|
||||
For some cases it can be useful to attach extensions to multiple parents. An example of this are Scaffolder field extensions or TechDocs addons that are consumed by multiple extensions. Specifying multiple attachments is done by providing an array of attachment points to the `attachTo` property of the extension. Keep in mind that this increases the complexity of your extension tree and should only be done when necessary. The following example shows how to attach our example extension to multiple parents:
|
||||
|
||||
```tsx
|
||||
const extension = createExtension({
|
||||
name: 'my-extension',
|
||||
attachTo: [
|
||||
{ id: 'my-first-parent', input: 'content' },
|
||||
{ id: 'my-second-parent', input: 'children' }, // The input names do not need to match
|
||||
],
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory() {
|
||||
return [coreExtensionData.reactElement(<div>Hello World</div>)];
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -168,5 +168,6 @@ We recommend you read [Setting up authentication](./authentication.md) next.
|
||||
If you want to read more about the database configuration, here are some helpful links:
|
||||
|
||||
- [Configuring Plugin Databases](../../tutorials/configuring-plugin-databases.md#privileges)
|
||||
- [Manual Knex Rollback](../../tutorials/manual-knex-rollback.md)
|
||||
- [Read more about Knex](http://knexjs.org/), the database wrapper that we use.
|
||||
- [Install `pgAdmin` 4](https://www.pgadmin.org/), a helpful tool for querying your database.
|
||||
|
||||
@@ -160,3 +160,7 @@ 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.
|
||||
|
||||
@@ -85,7 +85,7 @@ The parameters available are:
|
||||
- **`scope`** _(optional)_:
|
||||
`'global'` or `'local'`. Sets the scope of concurrency control.
|
||||
|
||||
_Note:_
|
||||
:::note Note
|
||||
|
||||
- The path parameter follows the same rules as the search on Azure DevOps web interface. For more details visit the [official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops).
|
||||
- To use branch parameters, it is necessary that the desired branch be added to the "Searchable branches" list within Azure DevOps Repositories. To do this, follow the instructions below:
|
||||
@@ -97,6 +97,8 @@ _Note:_
|
||||
5. In the window that appears, enter the name of the branch you want to add and click "Add".
|
||||
6. The added branch will now appear in the "Searchable branches" list.
|
||||
|
||||
:::
|
||||
|
||||
It may take some time before the branch is indexed and searchable.
|
||||
|
||||
As this provider is not one of the default providers, you will first need to install
|
||||
|
||||
@@ -121,9 +121,14 @@ catalog:
|
||||
workspace: workspace-name
|
||||
```
|
||||
|
||||
> **Note:** It is possible but certainly not recommended to skip the provider ID level.
|
||||
:::note Note
|
||||
|
||||
It is possible but certainly not recommended to skip the provider ID level.
|
||||
|
||||
> If you do so, `default` will be used as provider ID.
|
||||
|
||||
:::
|
||||
|
||||
- **`catalogPath`** _(optional)_:
|
||||
Default: `/catalog-info.yaml`.
|
||||
Path where to look for `catalog-info.yaml` files.
|
||||
|
||||
@@ -10,18 +10,12 @@ Backstage repository.
|
||||
## Creating a Backend Plugin
|
||||
|
||||
A new, bare-bones backend plugin package can be created by issuing the following
|
||||
command in your Backstage repository root:
|
||||
command in your Backstage repository root and selecting `backend-plugin`:
|
||||
|
||||
```sh
|
||||
yarn new --select backend-plugin
|
||||
yarn new
|
||||
```
|
||||
|
||||
Please also see the `--help` flag for the `new` command for some
|
||||
further options that are available, notably the `--scope` and `--no-private`
|
||||
flags that control naming and publishing of the newly created package. Your repo
|
||||
root `package.json` will probably also have some default values already set up
|
||||
for these.
|
||||
|
||||
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 example `carmen`, if it's a
|
||||
|
||||
@@ -15,9 +15,11 @@ invoking the
|
||||
from the root of your project.
|
||||
|
||||
```bash
|
||||
yarn new --select plugin
|
||||
yarn new
|
||||
```
|
||||
|
||||
And then select `frontend-plugin`.
|
||||
|
||||

|
||||
|
||||
This will create a new Backstage Plugin based on the ID that was provided. It
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!-- THIS FILE IS NOT INTENDED TO BE DISPLAYED ON THE DOCSITE -->
|
||||
|
||||
# Intro to Plugin Development
|
||||
|
||||
## Writing Guidelines
|
||||
|
||||
The goal of this section is to take the reader from zero-to-situated. Docs should assume that the user has _only_ gone through the initial getting started docs. At the end of this guide, the user will understand the basics of plugin development.
|
||||
|
||||
### Why build plugins?
|
||||
|
||||
This section should answer definitely why you should build a new plugin. The Backstage framework is deeply empowered by plugins and plugins are core to the project's success. Users should walk away from reading this section with a conviction that plugins are the right path for new functionality.
|
||||
|
||||
### Creating a plugin
|
||||
|
||||
This section should be extremely deliberate in showing readers every step of the way to create a plugin using Backstage's best practises. A reader that finishes this section should feel extremely comfortable adding and installing new plugins regardless of their experience with JS/TS and Backstage.
|
||||
|
||||
- Setting up your environment
|
||||
- Scaffolding a new plugin
|
||||
- Installation syntax
|
||||
- Core services
|
||||
- Debugging
|
||||
- Declaration error, `export default` missing
|
||||
- Startup error, `httpRouter` failed to start
|
||||
|
||||
### Sustainable plugin development
|
||||
|
||||
Plugins are not developed in a vacuum. Users should reach for them to solve specific business problems facing their developers, for example, you may be tasked to create
|
||||
|
||||
- a new vendor integration like PagerDuty,
|
||||
- a new plugin backend that talks to an internal service,
|
||||
- etc.
|
||||
|
||||
This section should contain learnings from successful Backstage deployments about how to engage with stakeholders, how/when to iterate on your plugin, and setting yourself up for future success.
|
||||
@@ -0,0 +1,18 @@
|
||||
<!-- THIS FILE IS NOT INTENDED TO BE DISPLAYED ON THE DOCSITE -->
|
||||
|
||||
## Glossary
|
||||
|
||||
- Page: A single `md` file.
|
||||
- Guide: A number of pages grouped under the same folder.
|
||||
- Progression: Guides are ordered and moving along that order will give you the next guide, for example, "201" will follow "101" or "experienced" will follow "beginner".
|
||||
- Progression level: Explicit guides in a progression, for example, "101" or "beginner"
|
||||
|
||||
## Writing Guidelines
|
||||
|
||||
The goal of these docs is to provide a comprehensive set of guides that developers + admins can use to quickly get up to speed with plugin development, and then refer to as they're developing their own plugins.
|
||||
|
||||
A user that finishes all of these guides will feel comfortable implementing plugins on their own. If additional assistance is required, they should be referred to other sources of information such as Discord, GitHub, source code, or documentation for further support. The user will also understand why/when to build their own plugins, inner-sourcing their developer portal and contributing internal plugins back to the open-source project.
|
||||
|
||||
At the same time, not all users will finish the docs or they may come back to them as required. Individual guides should have strong "abstracts" (what will I learn by reading this guide), table of contents, and "next steps" (what do I need to do next) to guide users to read the most important pieces for their work.
|
||||
|
||||
When writing guide pages, keep it light! These should be instructional docs, and at the same time conversational and a joy to read. Guides should build on each other, when reading through a progression, the reader should feel more comfortable and confident with concepts as they pop up across progression levels. Guides should be standalone, when finishing one level (for example 101), you should be able to immediately jump into the next (201) without additional research or background. Referencing previous progression levels is ok.
|
||||
@@ -21,7 +21,8 @@ should have a separate package in a folder, which represents your plugin.
|
||||
Example:
|
||||
|
||||
```
|
||||
$ yarn new --select plugin
|
||||
$ yarn new
|
||||
# Select `frontend-plugin`
|
||||
> ? Enter an ID for the plugin [required] my-plugin
|
||||
> ? Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -258,30 +258,28 @@ it is possible to pre-select what you want to create using the `--select` flag,
|
||||
and provide options using `--option`, for example:
|
||||
|
||||
```bash
|
||||
backstage-cli new --select plugin --option id=foo
|
||||
backstage-cli new --select plugin --option pluginId=foo
|
||||
```
|
||||
|
||||
This command is typically added as script in the root `package.json` to be
|
||||
executed with `yarn new`, using options that are appropriate for the organization
|
||||
that owns the app repo. For example you may have it set up like this:
|
||||
executed with `yarn new`. For example you may have it set up like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"new": "backstage-cli new --scope internal --no-private --npm-registry https://acme.org/npm"
|
||||
"new": "backstage-cli new"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `new` command comes with a default collection of plugins/packages, however,
|
||||
you can customize this list and even create your own CLI templates. For more
|
||||
information see [CLI Templates](./04-templates.md).
|
||||
|
||||
```text
|
||||
Usage: backstage-cli create [options]
|
||||
Usage: backstage-cli new
|
||||
|
||||
Options:
|
||||
--select <name> Select the thing you want to be creating upfront
|
||||
--option <name>=<value> Pre-fill options for the creation process (default: [])
|
||||
--scope <scope> The scope to use for new packages
|
||||
--npm-registry <URL> The package registry to use for new packages
|
||||
--no-private Do not mark new packages as private
|
||||
-h, --help display help for command
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
id: templates
|
||||
title: CLI Templates
|
||||
description: Overview of the new CLI Declarative Templates
|
||||
---
|
||||
|
||||
The behavior of the `backstage-cli new` command is configurable through your root `package.json`, and you can also create and add custom CLI templates to suit your needs.
|
||||
|
||||
## Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "root",
|
||||
"backstage": {
|
||||
"cli": {
|
||||
"new": {
|
||||
"globals": {
|
||||
"license": "MIT",
|
||||
"namePrefix": "@my-org/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `globals` - Configures input for all generated packages and plugins.
|
||||
- `version` - Sets the value of the `version` field in `package.json` of all generated packages. Defaults to `0.1.0`.
|
||||
- `license` - Sets the value of the `license` field in `package.json` of all generated packages. Defaults to `Apache-2.0`.
|
||||
- `private` - Sets the value of the `private` field in `package.json` of all generated packages. Defaults to `true`.
|
||||
- `publishRegistry` - Sets the value of the `publishConfig.registry` field in `package.json` of all generated packages.
|
||||
- `namePrefix` - The prefix used to generate the full package name. Defaults to `@internal/`.
|
||||
- `namePluginInfix` - The infix used to generate the full package name for plugin packages. Defaults to `plugin-`.
|
||||
- `templates` - Specifies custom templates.
|
||||
- See [Installing custom templates](#installing-custom-templates) and [Creating your own CLI templates](#creating-your-own-cli-templates) for more information.
|
||||
|
||||
The generated package name is based on the `namePrefix` and `namePluginInfix` globals, as well as the "base name" which is derived from the package role and user input. For plugin packages the final package name will be `<namePrefix><namePluginInfix><baseName>`, and for other packages it will be `<namePrefix><baseName>`.
|
||||
|
||||
For example, if you want your plugin frontend packages to end up with the name `@acme/backstage-plugin-<pluginId>`, you should use the following configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "root",
|
||||
"backstage": {
|
||||
"cli": {
|
||||
"new": {
|
||||
"globals": {
|
||||
"namePrefix": "@acme/",
|
||||
"namePluginInfix": "backstage-plugin-"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Installing custom templates
|
||||
|
||||
Custom templates can be installed from local directories. To install a template you add it to the `backstage.cli.new.templates` configuration array in your root `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "root",
|
||||
"backstage": {
|
||||
"cli": {
|
||||
"new": {
|
||||
"templates": ["./templates/custom-plugin"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each entry in the `templates` array should be a relative path that points to a directory containing a `portable-template.yaml` file. If the path starts with `./` it will be used as is, otherwise it will be resolved as a module within `node_modules`.
|
||||
|
||||
When defining the `templates` array it will override the default set of templates. If you want to keep using one of the build-in templates in the Backstage CLI you can reference them directly within the CLI package. This following is the full list of built-in templates:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "root",
|
||||
"backstage": {
|
||||
"cli": {
|
||||
"new": {
|
||||
"templates": [
|
||||
"@backstage/cli/templates/frontend-plugin",
|
||||
"@backstage/cli/templates/backend-plugin",
|
||||
"@backstage/cli/templates/backend-plugin-module",
|
||||
"@backstage/cli/templates/plugin-web-library",
|
||||
"@backstage/cli/templates/plugin-node-library",
|
||||
"@backstage/cli/templates/plugin-common-library",
|
||||
"@backstage/cli/templates/web-library",
|
||||
"@backstage/cli/templates/node-library",
|
||||
"@backstage/cli/templates/scaffolder-backend-module"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Creating your own CLI templates
|
||||
|
||||
Each template lives in its own directory and must have a `portable-template.yaml` file that describes the template. The template directory can also contain any files that should be templated or copied to the generated package.
|
||||
|
||||
Start by creating `portable-template.yaml` in a new directory somewhere in your project, in this example we're using `./templates/custom-plugin/portable-template.yaml`:
|
||||
|
||||
```yaml title="in templates/custom-plugin/portable-template.yaml"
|
||||
name: custom-plugin
|
||||
role: frontend-plugin
|
||||
description: Description of my CLI template # optional
|
||||
values: # optional
|
||||
pluginVar: '{{ camelCase pluginId }}Plugin'
|
||||
```
|
||||
|
||||
The following properties are supported:
|
||||
|
||||
- `name` **(required)** - The name of your template, used by the user to select it.
|
||||
- `role` **(required)** - The role of the template, similar to package role. See [Template Roles](#template-roles) for more details.
|
||||
- `description` - A description of the type of package that this template produces.
|
||||
- `values` - A map of additional values that will be present during templating. The values are themselves templated and can reference other values. If the key matches any of the user prompts, such as `pluginId`, the value will be used directly instead of prompting the user.
|
||||
|
||||
Next, add any other files you want to be part of the template to the same directory. All files will be copied as is, except any files with a `.hbs` extension. They will be treated as [Handlebars](https://handlebarsjs.com/) templates and will be rendered with the values from the `portable-template.yaml` file as well as additional prompts such as `pluginId`. For example, you could create a `src/index.ts` file with the following content:
|
||||
|
||||
```typescript title="in templates/custom-plugin/src/index.ts.hbs"
|
||||
export function getPluginId() {
|
||||
return '{{ pluginId }}';
|
||||
}
|
||||
```
|
||||
|
||||
If you'd like to see more examples, you can find all the default templates and their yaml files [here](https://github.com/backstage/backstage/tree/master/packages/cli/templates).
|
||||
|
||||
Once your template is ready, [add it to your config](#installing-custom-templates), and you should now be able to select it when running `yarn new`.
|
||||
|
||||
### Template Roles
|
||||
|
||||
The `role` property in the template yaml file is used to determine what input will be gathered for the template, as well as what actions will be taken after the new package has been created. The following roles are supported:
|
||||
|
||||
| Role | Prompts | Output Directory | Additional Actions |
|
||||
| :----------------------- | :--------------------- | :--------------- | :-------------------------------------------------------------------------------- |
|
||||
| `frontend-plugin` | `pluginId` | `plugins` | Add dependency to `packages/app` and entry to `packages/backend/src/App.tsx` |
|
||||
| `frontend-plugin-module` | `pluginId`, `moduleId` | `plugins` | Add dependency to `packages/app` |
|
||||
| `backend-plugin` | `pluginId` | `plugins` | Add dependency to `packages/backend` and entry to `packages/backend/src/index.ts` |
|
||||
| `backend-plugin-module` | `pluginId`, `moduleId` | `plugins` | Add dependency to `packages/backend` and entry to `packages/backend/src/index.ts` |
|
||||
| `web-library` | `name` | `packages` | none |
|
||||
| `node-library` | `name` | `packages` | none |
|
||||
| `common-library` | `name` | `packages` | none |
|
||||
| `plugin-web-library` | `pluginId` | `plugins` | none |
|
||||
| `plugin-node-library` | `pluginId` | `plugins` | none |
|
||||
| `plugin-common-library` | `pluginId` | `plugins` | none |
|
||||
@@ -181,3 +181,11 @@ GRANT SHOW DATABASES ON *.* TO some_user;
|
||||
|
||||
The mechanisms in this guide should help you tackle different database
|
||||
deployment situations. Good luck!
|
||||
|
||||
## Further Reading
|
||||
|
||||
If you want to read more about the database configuration, here are some helpful links:
|
||||
|
||||
- [Manual Knex Rollback](./manual-knex-rollback.md)
|
||||
- [Read more about Knex](http://knexjs.org/), the database wrapper that we use.
|
||||
- [Install `pgAdmin` 4](https://www.pgadmin.org/), a helpful tool for querying your database.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
id: manual-knex-rollback
|
||||
title: Manual Rollback using Knex
|
||||
# prettier-ignore
|
||||
description: Guide on how to rollback Knex migrations.
|
||||
---
|
||||
|
||||
The most common case to use Knex directly is when you want to **rollback a migration** that was applied when you upgraded your Backstage instance and want to downgrade due to some problem. You can use the `migrate:down` command to rollback a specific migration. You can also use the `migrate:rollback` command to rollback the last batch of migrations. This is necessary because Knex will mark migrations as corrupted if you try to downgrade your Backstage instance without the rollback. Be aware to run those commands in the new version of the Backstage instance, so you can avoid the corrupted migrations for lower versions.
|
||||
|
||||
You are likely to receive a message like this when you try a downgrade without the rollback:
|
||||
|
||||
```sh
|
||||
Backend failed to start up Error: The migration directory is corrupt, the following files are missing: 20230428155633_sessions.js
|
||||
```
|
||||
|
||||
Currently, we don't have a simple way to check which migrations and which plugins have been applied in the database, but you can follow this [issue](https://github.com/backstage/backstage/issues/22439) to get more information about this.
|
||||
This guide covers a simple way to rollback migrations using Knex. We have plans to support this in Backstage's CLI ([issue](https://github.com/backstage/backstage/issues/6366)), but for now it is possible to use Knex CLI to manage migrations in necessary cases.
|
||||
|
||||
To start, you are going to need two things: database access and the plugin migrations directory you want to handle. We are going to use environment variables to access the database and we'll be using the `@backstage/plugin-catalog-backend` plugin as an example. In most cases, there is a `migrations` directory in the root of the plugin package, but you can check the `package.json` file to confirm the directory.
|
||||
You can get more information about how Backstage handles Databases in [Configuring Plugin Databases](./configuring-plugin-databases.md). This tutorial follows the information in the [Knex migration guide](https://knexjs.org/guide/migrations.html), so you can get more details about the commands there.
|
||||
|
||||
You can interact with Knex running the commands below in the project root:
|
||||
|
||||
We want to check the migration status:
|
||||
|
||||
```sh
|
||||
$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/
|
||||
Using environment: production
|
||||
Found 2 Completed Migration file/files.
|
||||
20211229105307_init.js
|
||||
20240113144027_assets-namespace.js
|
||||
No Pending Migration files Found.
|
||||
```
|
||||
|
||||
Now lets rollback a specific migration called `20240113144027_assets-namespace.js`:
|
||||
|
||||
```sh
|
||||
$ node_modules/.bin/knex migrate:down 20240113144027_assets-namespace.js --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/
|
||||
Using environment: production
|
||||
Batch 2 rolled back the following migrations:
|
||||
20240113144027_assets-namespace.js
|
||||
```
|
||||
|
||||
Now we can check the migration status again to confirm the rollback:
|
||||
|
||||
```sh
|
||||
$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/
|
||||
Using environment: production
|
||||
Found 1 Completed Migration file/files.
|
||||
20211229105307_init.js
|
||||
Found 1 Pending Migration file/files.
|
||||
20240113144027_assets-namespace.js
|
||||
```
|
||||
|
||||
Now lets use `migrate:currentVersion` which retrieves the current migration version. If there aren't any migrations run yet, it will return "none".
|
||||
|
||||
```sh
|
||||
$ node_modules/.bin/knex migrate:currentVersion --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg
|
||||
Using environment: production
|
||||
Current Version: 20240113144027
|
||||
```
|
||||
@@ -30,7 +30,7 @@ title: Adding Custom Plugin to Existing Monorepo App
|
||||
# The Skeleton Plugin
|
||||
|
||||
1. Start by using the built-in creator. From the terminal and root of your
|
||||
project run: `yarn new --select plugin`
|
||||
project run: `yarn new` and select `frontend-plugin`.
|
||||
1. Enter a plugin ID. I used `github-playground`
|
||||
1. When the process finishes, let's start the backend:
|
||||
`yarn --cwd packages/backend start`
|
||||
|
||||
@@ -181,13 +181,9 @@ It's crucial that you update to `RequirePermission` at the same time as you upda
|
||||
When migrating over to React Router v6 stable, you might also see browser console warnings for the `Navigate` component. This will need to be wrapped up in a `Route` component with the `Navigate` component in the `element` prop.
|
||||
|
||||
```tsx
|
||||
{
|
||||
/* highlight-remove-next-line */
|
||||
}
|
||||
{/* prettier-ignore */ /* highlight-remove-next-line */}
|
||||
<Navigate key="/" to="catalog" />;
|
||||
{
|
||||
/* highlight-add-next-line */
|
||||
}
|
||||
{/* prettier-ignore */ /* highlight-add-next-line */}
|
||||
<Route path="/" element={<Navigate to="catalog" />} />;
|
||||
```
|
||||
|
||||
|
||||
@@ -479,6 +479,7 @@ module.exports = {
|
||||
'tooling/cli/overview',
|
||||
'tooling/cli/build-system',
|
||||
'tooling/cli/commands',
|
||||
'tooling/cli/templates',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Local Development',
|
||||
@@ -498,6 +499,7 @@ module.exports = {
|
||||
Technical: [
|
||||
'tutorials/quickstart-app-plugin',
|
||||
'tutorials/configuring-plugin-databases',
|
||||
'tutorials/manual-knex-rollback',
|
||||
'tutorials/switching-sqlite-postgres',
|
||||
'tutorials/using-backstage-proxy-within-plugin',
|
||||
'tutorials/enable-public-entry',
|
||||
|
||||
+3
-3
@@ -10796,11 +10796,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"serialize-javascript@npm:^6.0.0, serialize-javascript@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "serialize-javascript@npm:6.0.1"
|
||||
version: 6.0.2
|
||||
resolution: "serialize-javascript@npm:6.0.2"
|
||||
dependencies:
|
||||
randombytes: ^2.1.0
|
||||
checksum: 3c4f4cb61d0893b988415bdb67243637333f3f574e9e9cc9a006a2ced0b390b0b3b44aef8d51c951272a9002ec50885eefdc0298891bc27eb2fe7510ea87dc4f
|
||||
checksum: c4839c6206c1d143c0f80763997a361310305751171dd95e4b57efee69b8f6edd8960a0b7fbfc45042aadff98b206d55428aee0dc276efe54f100899c7fa8ab7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ nav:
|
||||
- Builtin Actions: 'features/software-templates/builtin-actions.md'
|
||||
- Writing Custom Actions: 'features/software-templates/writing-custom-actions.md'
|
||||
- Writing Custom Step Layouts: 'features/software-templates/writing-custom-step-layouts.md'
|
||||
- Template Extensions: 'features/software-templates/template-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:
|
||||
@@ -211,6 +212,7 @@ nav:
|
||||
- Package Role Migration: 'tutorials/package-role-migration.md'
|
||||
- Migrating away from @backstage/core: 'tutorials/migrating-away-from-core.md'
|
||||
- Adding Custom Plugin to Existing Monorepo App: 'tutorials/quickstart-app-plugin.md'
|
||||
- Manual Rollback using Knex: 'tutorials/manual-knex-rollback.md'
|
||||
- Switching Backstage from SQLite to PostgreSQL: 'tutorials/switching-sqlite-postgres.md'
|
||||
- Using the Backstage Proxy from Within a Plugin: 'tutorials/using-backstage-proxy-within-plugin.md'
|
||||
- Migration to Yarn 3: 'tutorials/yarn-migration.md'
|
||||
|
||||
+12
-2
@@ -1,11 +1,21 @@
|
||||
{
|
||||
"name": "root",
|
||||
"version": "1.36.0-next.2",
|
||||
"version": "1.36.0-next.3",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage"
|
||||
},
|
||||
"backstage": {
|
||||
"cli": {
|
||||
"new": {
|
||||
"globals": {
|
||||
"private": false,
|
||||
"namePrefix": "@backstage/"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*",
|
||||
@@ -33,7 +43,7 @@
|
||||
"lint:docs": "node ./scripts/check-docs-quality",
|
||||
"lint:peer-deps": "backstage-repo-tools peer-deps",
|
||||
"lint:type-deps": "backstage-repo-tools type-deps",
|
||||
"new": "backstage-cli new --scope backstage --baseVersion 0.0.0 --no-private",
|
||||
"new": "backstage-cli new",
|
||||
"prepare": "husky",
|
||||
"prettier:check": "prettier --check .",
|
||||
"prettier:fix": "prettier --write .",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# app-next-example-plugin
|
||||
|
||||
## 0.0.20-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/frontend-plugin-api@0.9.5-next.3
|
||||
- @backstage/core-components@0.16.4-next.1
|
||||
|
||||
## 0.0.20-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-next-example-plugin",
|
||||
"version": "0.0.20-next.2",
|
||||
"version": "0.0.20-next.3",
|
||||
"description": "Backstage internal example plugin",
|
||||
"backstage": {
|
||||
"role": "frontend-plugin",
|
||||
|
||||
@@ -1,5 +1,50 @@
|
||||
# example-app-next
|
||||
|
||||
## 0.0.20-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog@1.27.0-next.3
|
||||
- @backstage/cli@0.30.0-next.3
|
||||
- @backstage/plugin-scaffolder@1.28.0-next.3
|
||||
- @backstage/frontend-plugin-api@0.9.5-next.3
|
||||
- @backstage/frontend-app-api@0.10.5-next.3
|
||||
- @backstage/core-compat-api@0.3.6-next.3
|
||||
- @backstage/plugin-api-docs@0.12.4-next.3
|
||||
- @backstage/plugin-catalog-graph@0.4.16-next.3
|
||||
- @backstage/plugin-catalog-import@0.12.10-next.3
|
||||
- @backstage/plugin-org@0.6.36-next.3
|
||||
- @backstage/plugin-scaffolder-react@1.14.5-next.3
|
||||
- @backstage/plugin-techdocs@1.12.3-next.3
|
||||
- @backstage/plugin-user-settings@0.8.19-next.3
|
||||
- @backstage/app-defaults@1.5.17-next.1
|
||||
- @backstage/catalog-model@1.7.3
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/core-app-api@1.15.5-next.0
|
||||
- @backstage/core-components@0.16.4-next.1
|
||||
- @backstage/core-plugin-api@1.10.4-next.0
|
||||
- @backstage/frontend-defaults@0.1.6-next.3
|
||||
- @backstage/integration-react@1.2.4-next.0
|
||||
- @backstage/theme@0.6.4-next.0
|
||||
- @backstage/plugin-app@0.1.6-next.3
|
||||
- @backstage/plugin-app-visualizer@0.1.16-next.3
|
||||
- @backstage/plugin-auth-react@0.1.12-next.1
|
||||
- @backstage/plugin-catalog-common@1.1.3
|
||||
- @backstage/plugin-catalog-react@1.15.2-next.3
|
||||
- @backstage/plugin-catalog-unprocessed-entities@0.2.14-next.1
|
||||
- @backstage/plugin-home@0.8.5-next.3
|
||||
- @backstage/plugin-kubernetes@0.12.4-next.3
|
||||
- @backstage/plugin-kubernetes-cluster@0.0.22-next.3
|
||||
- @backstage/plugin-notifications@0.5.2-next.2
|
||||
- @backstage/plugin-permission-react@0.4.31-next.0
|
||||
- @backstage/plugin-search@1.4.23-next.3
|
||||
- @backstage/plugin-search-common@1.2.17
|
||||
- @backstage/plugin-search-react@1.8.6-next.3
|
||||
- @backstage/plugin-signals@0.0.16-next.1
|
||||
- @backstage/plugin-techdocs-module-addons-contrib@1.1.21-next.1
|
||||
- @backstage/plugin-techdocs-react@1.2.14-next.1
|
||||
|
||||
## 0.0.20-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-app-next",
|
||||
"version": "0.0.20-next.2",
|
||||
"version": "0.0.20-next.3",
|
||||
"backstage": {
|
||||
"role": "frontend"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,46 @@
|
||||
# example-app
|
||||
|
||||
## 0.2.106-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog@1.27.0-next.3
|
||||
- @backstage/cli@0.30.0-next.3
|
||||
- @backstage/plugin-scaffolder@1.28.0-next.3
|
||||
- @backstage/frontend-app-api@0.10.5-next.3
|
||||
- @backstage/plugin-api-docs@0.12.4-next.3
|
||||
- @backstage/plugin-catalog-graph@0.4.16-next.3
|
||||
- @backstage/plugin-catalog-import@0.12.10-next.3
|
||||
- @backstage/plugin-org@0.6.36-next.3
|
||||
- @backstage/plugin-scaffolder-react@1.14.5-next.3
|
||||
- @backstage/plugin-techdocs@1.12.3-next.3
|
||||
- @backstage/plugin-user-settings@0.8.19-next.3
|
||||
- @backstage/app-defaults@1.5.17-next.1
|
||||
- @backstage/catalog-model@1.7.3
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/core-app-api@1.15.5-next.0
|
||||
- @backstage/core-components@0.16.4-next.1
|
||||
- @backstage/core-plugin-api@1.10.4-next.0
|
||||
- @backstage/integration-react@1.2.4-next.0
|
||||
- @backstage/theme@0.6.4-next.0
|
||||
- @backstage/plugin-auth-react@0.1.12-next.1
|
||||
- @backstage/plugin-catalog-common@1.1.3
|
||||
- @backstage/plugin-catalog-react@1.15.2-next.3
|
||||
- @backstage/plugin-catalog-unprocessed-entities@0.2.14-next.1
|
||||
- @backstage/plugin-devtools@0.1.24-next.3
|
||||
- @backstage/plugin-home@0.8.5-next.3
|
||||
- @backstage/plugin-kubernetes@0.12.4-next.3
|
||||
- @backstage/plugin-kubernetes-cluster@0.0.22-next.3
|
||||
- @backstage/plugin-notifications@0.5.2-next.2
|
||||
- @backstage/plugin-permission-react@0.4.31-next.0
|
||||
- @backstage/plugin-search@1.4.23-next.3
|
||||
- @backstage/plugin-search-common@1.2.17
|
||||
- @backstage/plugin-search-react@1.8.6-next.3
|
||||
- @backstage/plugin-signals@0.0.16-next.1
|
||||
- @backstage/plugin-techdocs-module-addons-contrib@1.1.21-next.1
|
||||
- @backstage/plugin-techdocs-react@1.2.14-next.1
|
||||
|
||||
## 0.2.106-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.2.106-next.2",
|
||||
"version": "0.2.106-next.3",
|
||||
"backstage": {
|
||||
"role": "frontend"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @backstage/backend-app-api
|
||||
|
||||
## 1.2.0-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-permission-node@0.8.8-next.2
|
||||
- @backstage/backend-plugin-api@1.2.0-next.2
|
||||
- @backstage/config-loader@1.9.6-next.0
|
||||
- @backstage/plugin-auth-node@0.6.0-next.2
|
||||
- @backstage/cli-common@0.1.15
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/errors@1.2.7
|
||||
- @backstage/types@1.2.1
|
||||
|
||||
## 1.2.0-next.2
|
||||
|
||||
### Minor Changes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user