Merge branch 'backstage:master' into mysql8-catalog-backend

This commit is contained in:
Alex
2022-10-27 18:56:32 -07:00
committed by GitHub
850 changed files with 14656 additions and 4506 deletions
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/plugin-scaffolder': patch
---
Basic analytics instrumentation is now in place:
- As users make their way through template steps, a `click` event is fired, including the step number.
- After a user clicks "Create" a `create` event is fired, including the name of the software that was just created. The template used at creation is set on the `entityRef` context key.
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': patch
---
Both `EntityProvider` and `AsyncEntityProvider` contexts now wrap all children with an `AnalyticsContext` containing the corresponding `entityRef`; this opens up the possibility for all events underneath these contexts to be associated with and aggregated by the corresponding entity.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
Fix logic bug that broke techdocs-cli-embedded-app
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-app-api': minor
---
Updated the React Router wiring to make use of the new `basename` property of the router components in React Router v6 stable. To implement this, a new optional `basename` property has been added to the `Router` app component, which can be forwarded to the concrete router implementation in order to support this new behavior. This is done by default in any app that does not have a `Router` component override.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/backend-common': minor
---
**BREAKING CHANGE**: The `UrlReader` interface has been updated to require that `readUrl` is implemented. `readUrl` has previously been optional to implement but a warning has been logged when calling its predecessor `read`.
The `read` method is now deprecated and will be removed in a future release.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-github-issues': patch
---
Stripping specific issues URL already present to target base issues URL.
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-github-pull-requests-board': patch
---
Replace the momentjs dependency with luxon.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/plugin-catalog-backend-module-msgraph': patch
---
`MicrosoftGraphOrgEntityProvider`: Add option to configure schedule via `app-config.yaml` instead of in code.
Please find how to configure the schedule at the config at
https://github.com/backstage/backstage/tree/master/plugins/catalog-backend-module-msgraph#readme
+25
View File
@@ -0,0 +1,25 @@
---
'@backstage/plugin-techdocs': minor
---
Add ability to configure filters when using EntityListDocsGrid
The following example will render two sections of cards grid:
- One section for documentations tagged as `recommended`
- One section for documentations tagged as `runbook`
```js
<EntityListDocsGrid groups={{[
{
title: "Recommended Documentation",
filterPredicate: entity =>
entity?.metadata?.tags?.includes('recommended') ?? false,
},
{
title: "RunBooks Documentation",
filterPredicate: entity =>
entity?.metadata?.tags?.includes('runbook') ?? false,
}
]}} />
```
+17
View File
@@ -0,0 +1,17 @@
---
'@backstage/plugin-catalog-backend-module-github': minor
---
BREAKING: Support authenticated backends by including a server token for catalog requests. The constructor of `GithubLocationAnalyzer` now requires an instance of `TokenManager` to be supplied:
```diff
...
builder.addLocationAnalyzers(
new GitHubLocationAnalyzer({
discovery: env.discovery,
config: env.config,
+ tokenManager: env.tokenManager,
}),
);
...
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-plugin-api': minor
---
The app `Router` component now accepts an optional `basename` property.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-catalog-backend': patch
'@backstage/plugin-techdocs-node': patch
'@backstage/plugin-techdocs-backend': patch
---
Replace usage of deprecataed `UrlReader.read` with `UrlReader.readUrl`.
+38
View File
@@ -0,0 +1,38 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
Added a set of default Prometheus metrics around scaffolding. See below for a list of metrics and an explanation of their labels:
- `scaffolder_task_count`: Tracks successful task runs.
Labels:
- `template`: The entity ref of the scaffolded template
- `user`: The entity ref of the user that invoked the template run
- `result`: A string describing whether the task ran successfully, failed, or was skipped
- `scaffolder_task_duration`: a histogram which tracks the duration of a task run
Labels:
- `template`: The entity ref of the scaffolded template
- `result`: A boolean describing whether the task ran successfully
- `scaffolder_step_count`: a count that tracks each step run
Labels:
- `template`: The entity ref of the scaffolded template
- `step`: The name of the step that was run
- `result`: A string describing whether the task ran successfully, failed, or was skipped
- `scaffolder_step_duration`: a histogram which tracks the duration of each step run
Labels:
- `template`: The entity ref of the scaffolded template
- `step`: The name of the step that was run
- `result`: A string describing whether the task ran successfully, failed, or was skipped
You can find a guide for running Prometheus metrics here: https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/prometheus-metrics.md
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
Generated development HTTPS backend certificate is now checked for expiration date instead of file age.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/integration': minor
---
This patch brings Gitea as a valid integration: target, via the ScmIntegration interface. It adds gitea to the relevant static properties (get integration by name, get integration by type) for plugins to be able to reference the same Gitea server.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
This patch adds GiteaURLReader to the available classes. It currently only reads single files via gitea's public repos api
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/plugin-auth-backend': patch
'@backstage/plugin-cost-insights': patch
'@backstage/plugin-dynatrace': patch
'@backstage/plugin-stack-overflow-backend': patch
'@backstage/plugin-techdocs': patch
---
Tweak README
+14
View File
@@ -0,0 +1,14 @@
---
'@backstage/integration': minor
---
Replaces in-code uses of `GitHub` with `Github` and deprecates old versions.
Deprecates:
- `getGitHubFileFetchUrl` replaced by `getGithubFileFetchUrl`
- `GitHubIntegrationConfig` replaced by `GithubIntegrationConfig`
- `GitHubIntegration` replaced by `GithubIntegration`
- `readGitHubIntegrationConfig` replaced by `readGithubIntegrationConfig`
- `readGitHubIntegrationConfigs` replaced by `readGithubIntegrationConfigs`
- `replaceGitHubUrlType` replaced by `replaceGithubUrlType`
+16
View File
@@ -0,0 +1,16 @@
---
'@backstage/cli': minor
---
Added a new ESLint rule that restricts imports of Link from @material-ui
The rule can be can be overridden in the following way:
```diff
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
+ restrictedImports: [
+ { name: '@material-ui/core', importNames: [] },
+ { name: '@material-ui/core/Link', importNames: [] },
+ ],
});
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-import': patch
---
Get rid of `this-is-undefined-in-esm` warning
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': patch
---
Removed forced capitalization for Entity types in the catalog sidebar.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-tasks': patch
---
Deprecated the `HumanDuration` type, which should now instead be imported from `@backstage/types`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-radar': patch
---
Fixed bug in Tech Radar where, on hover, the tech list quadrant would rerender and scroll top
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
Small update to fix compatibility with newer versions of the `keyv` library
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Updated `@swc/core` to version 1.3.9 which fixes a `.tsx` parser bug
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-msgraph': patch
---
Add `microsoftGraphOrgEntityProviderCatalogModule` (new backend-plugin-api, alpha).
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/plugin-github-pull-requests-board': patch
---
Add a new "Team" Filter Options to the Github Pull Requests Dashboard.
When toggling this option on, the dashboard will displays all of the PRs opened
by the members of that team on any repositories of the organization.
+14
View File
@@ -0,0 +1,14 @@
---
'@backstage/catalog-model': patch
'@backstage/core-components': patch
'@backstage/integration': patch
'@backstage/plugin-auth-node': patch
'@backstage/plugin-gcalendar': patch
'@backstage/plugin-periskop': patch
'@backstage/plugin-permission-common': patch
'@backstage/plugin-scaffolder-backend': patch
'@backstage/plugin-techdocs': patch
'@backstage/plugin-xcmetrics': patch
---
Internal refactor of imports to avoid circular dependencies
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-github': patch
---
Properly derive Github credentials when making requests in `GithubLocationAnalyzer` to support Github App authentication
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-ilert': minor
---
Added support for multiple responders in alert list, added new tab with list to support iLert resource 'service', added new tab with list to support iLert resource 'status page'
+70 -2
View File
@@ -173,7 +173,75 @@
"@backstage/plugin-user-settings-backend": "0.1.1",
"@backstage/plugin-vault": "0.1.4",
"@backstage/plugin-vault-backend": "0.2.3",
"@backstage/plugin-xcmetrics": "0.2.30"
"@backstage/plugin-xcmetrics": "0.2.30",
"@backstage/plugin-azure-sites": "0.0.0",
"@backstage/plugin-azure-sites-backend": "0.0.0",
"@backstage/plugin-azure-sites-common": "0.0.0"
},
"changesets": []
"changesets": [
"analyze-software-creation",
"analyze-software-exploration",
"big-islands-add",
"brave-eels-allow",
"brown-days-pretend",
"calm-bottles-happen",
"chatty-planets-flash",
"clean-feet-remain",
"clean-planets-rhyme",
"dirty-birds-burn",
"dull-oranges-tap",
"eight-pears-attack",
"eleven-pets-sneeze",
"few-books-remember",
"flat-items-perform",
"flat-kangaroos-kiss",
"forty-bags-trade",
"forty-jokes-lie",
"fresh-cooks-sing",
"fresh-weeks-share",
"gorgeous-balloons-sit",
"gorgeous-onions-thank",
"gorgeous-queens-pull",
"great-colts-invite",
"grumpy-pigs-reflect",
"happy-avocados-tan",
"heavy-elephants-nail",
"itchy-paws-protect",
"kind-emus-juggle",
"lazy-planes-repair",
"little-bikes-eat",
"lucky-cats-peel",
"lucky-spoons-hide",
"mean-files-fly",
"metal-dogs-swim",
"nasty-crabs-share",
"orange-trees-peel",
"popular-bulldogs-lie",
"popular-mails-wave",
"real-swans-repair",
"renovate-6fb5f1b",
"selfish-kiwis-matter",
"shaggy-birds-happen",
"shaggy-colts-watch",
"sharp-goats-itch",
"shiny-beers-relax",
"short-balloons-work",
"sixty-islands-develop",
"sixty-pigs-shave",
"sixty-singers-push",
"spicy-parents-lick",
"spotty-dryers-explain",
"stupid-pens-occur",
"sweet-readers-compare",
"tame-ads-appear",
"tasty-colts-hug",
"tasty-scissors-tickle",
"ten-pens-draw",
"three-houses-agree",
"three-poems-think",
"two-oranges-joke",
"two-yaks-wave",
"unlucky-buttons-poke",
"wet-cameras-call"
]
}
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-permission-common': patch
---
Properly handle rules that have no parameters in `PermissionClient`
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/cli': patch
---
Updated dependency `@svgr/plugin-jsx` to `6.5.x`.
Updated dependency `@svgr/plugin-svgo` to `6.5.x`.
Updated dependency `@svgr/rollup` to `6.5.x`.
Updated dependency `@svgr/webpack` to `6.5.x`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
Updated `@swc/core` to `v1.3.9` which fixes a `.tsx` parser bug. You may want to run `yarn backstage-cli versions:bump` to get on latest version including the CLI itself.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-github-issues': minor
---
**BREAKING**: Changed the casing of all exported types to have a lowercase "h" in "github". E.g. "GitHubIssuesPage" was renamed to "GithubIssuesPage". Please rename your imports where necessary.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Disable base path workaround in `Link` component when React Router v6 stable is used.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/types': patch
---
Added the `HumanDuration` type, moved here from `@backstage/backend-tasks`. This type matches the `Duration.fromObject` form of `luxon`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
Updated the create-app command to no longer require Git to be installed and configured. A git repository will only be initialized if possible and if not already in an git repository.
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/plugin-bitrise': patch
'@backstage/plugin-code-coverage': patch
'@backstage/plugin-cost-insights': minor
'@backstage/plugin-git-release-manager': patch
'@backstage/plugin-xcmetrics': patch
---
Updated recharts to v2.0.0 and fixed typing issues
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder': minor
---
Added props to override default Scaffolder page title, subtitle and pageTitleOverride.
Routes like `rootRouteRef`, `selectedTemplateRouteRef`, `nextRouteRef`, `nextSelectedTemplateRouteRef` were made public and can be used in your app (e.g. in custom TemplateCard component).
+13
View File
@@ -0,0 +1,13 @@
---
'@backstage/backend-common': patch
'@backstage/plugin-catalog-backend-module-github': patch
'@backstage/plugin-catalog-import': patch
'@backstage/plugin-git-release-manager': patch
'@backstage/plugin-github-actions': patch
'@backstage/plugin-github-issues': patch
'@backstage/plugin-github-pull-requests-board': patch
'@backstage/plugin-techdocs': patch
'@backstage/plugin-techdocs-module-addons-contrib': patch
---
Replaces in-code uses of `GitHub` with `Github` and deprecates old versions.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-bazaar': minor
---
The limit prop of BazaarOverviewCard has been removed entirely, and instead replaced with a new optional boolean prop `fullWidth`. The BazaarOverviewCard now always use full height without fixed width. Also fixed problem with link to Bazaar.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-xcmetrics': patch
---
Removed an unused and hidden build details route.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-circleci': patch
---
Update screenshots in documentation to match latest CircleCI plugin
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-tech-insights-backend': patch
'@backstage/plugin-tech-insights-node': patch
---
Add a default delay to the fact retrievers to prevent cold-start errors
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-tech-insights-backend': patch
'@backstage/plugin-tech-insights-node': patch
---
Use `HumanDuration` from `@backstage/types`
+14
View File
@@ -0,0 +1,14 @@
---
'@backstage/plugin-catalog-backend-module-github': minor
---
Added the ability for the GitHub discovery provider to validate that catalog files exist before emitting them.
Users can now set the `validateLocationsExist` property to `true` in their GitHub discovery configuration to opt in to this feature.
This feature only works with `catalogPath`s that do not contain wildcards.
When `validateLocationsExist` is set to `true`, the GitHub discovery provider will retrieve the object from the
repository at the provided `catalogPath`.
If this file exists and is non-empty, then it will be emitted as a location for further processing.
If this file does not exist or is empty, then it will not be emitted.
Not emitting locations that do not exist allows for far fewer calls to the GitHub API to validate locations that do not exist.
+20
View File
@@ -0,0 +1,20 @@
---
'@backstage/core-components': patch
'@backstage/create-app': patch
'@backstage/plugin-azure-sites': patch
'@backstage/plugin-catalog': patch
'@backstage/plugin-circleci': patch
'@backstage/plugin-cloudbuild': patch
'@backstage/plugin-dynatrace': patch
'@backstage/plugin-gcalendar': patch
'@backstage/plugin-github-actions': patch
'@backstage/plugin-github-issues': patch
'@backstage/plugin-gitops-profiles': patch
'@backstage/plugin-jenkins': patch
'@backstage/plugin-kafka': patch
'@backstage/plugin-pagerduty': patch
'@backstage/plugin-rollbar': patch
'@backstage/plugin-scaffolder': patch
---
Prefer using `Link` from `@backstage/core-components` rather than material-UI.
+15
View File
@@ -0,0 +1,15 @@
---
'@backstage/integration': patch
'@backstage/plugin-catalog-backend-module-aws': patch
'@backstage/plugin-catalog-backend-module-gerrit': patch
'@backstage/plugin-stack-overflow-backend': patch
'@backstage/plugin-techdocs-backend': patch
---
Remove explicit default visibility at `config.d.ts` files.
```ts
/**
* @visibility backend
*/
```
+19
View File
@@ -1,5 +1,6 @@
abc
accessors
ACLs
addon
addons
ADRs
@@ -17,6 +18,7 @@ autoscaling
Autoscaling
autoselect
Avro
backend's
backported
backporting
Bigtable
@@ -88,6 +90,8 @@ dockerfiles
Dockerize
dockerode
Docusaurus
DOMPurify
don'ts
dynatrace
Dynatrace
ecco
@@ -96,6 +100,8 @@ Env
elasticsearch
esbuild
eslint
ESModule
ESModules
etag
Expedia
facto
@@ -115,6 +121,8 @@ github
Gitiles
gitlab
GitLab
gitea
Gitea
Gource
Grafana
graphql
@@ -136,8 +144,10 @@ iLert
img
incentivised
Indal
indexable
inlined
inlinehilite
integrator's
interop
JaCoCo
JavaScript
@@ -168,6 +178,7 @@ lunr
Luxon
magiclink
mailto
maintainer's
maintainership
makefile
md
@@ -222,6 +233,7 @@ orgs
pagerduty
pageview
parallelization
parseable
Patrik
Peloton
performant
@@ -306,6 +318,10 @@ stringify
stringified
subcomponent
subcomponents
subfolder
subfolders
subheader
subheaders
subkey
subroutes
subtree
@@ -326,6 +342,7 @@ templater
Templater
templaters
Templaters
TFRecord
theia
thumbsup
todo
@@ -342,6 +359,7 @@ transpiled
transpiler
transpilers
truthy
TSDoc
typeahead
ui
unbreak
@@ -364,6 +382,7 @@ VSCode
Wayfair
Weaveworks
Webpack
widget's
winston
www
WWW
+2 -1
View File
@@ -22,7 +22,8 @@ jobs:
stale-pr-message: >
This PR has been automatically marked as stale because it has not had
recent activity from the author. It will be closed if no further activity occurs.
If you are the author and the PR has been closed, feel free to re-open the PR and continue the contribution!
If the PR was closed and you want it re-opened, let us know
and we'll re-open the PR so that you can continue the contribution!
days-before-pr-stale: 7
days-before-pr-close: 5
exempt-pr-labels: after-vacations,will-fix
@@ -1,5 +1,6 @@
name: Sync Snyk GitHub issues
on:
workflow_dispatch:
schedule:
- cron: '0 */4 * * *'
@@ -13,9 +14,6 @@ jobs:
matrix:
node-version: [14.x]
env:
NODE_OPTIONS: --max-old-space-size=4096
steps:
- uses: actions/checkout@v3
@@ -38,9 +36,11 @@ jobs:
--org=backstage-dgh
--strict-out-of-sync=false
--json-file-output=snyk.json
--debug
json: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
NODE_OPTIONS: --max-old-space-size=7168
- name: Update Github issues
run: yarn ts-node scripts/snyk-github-issue-sync.ts
env:
+4 -3
View File
@@ -6,7 +6,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| Organization | Contact | Description of Use |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. |
| [bol.com](https://www.bol.com) | [@sagacity](https://github.com/sagacity) | Initial work being done to unify platform tooling. |
| [bol.com](https://www.bol.com) | [@acierto](https://github.com/acierto), [@clanghout](https://github.com/clanghout) | Initial work being done to unify platform tooling. |
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
@@ -25,7 +25,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [Expedia Group](https://www.expediagroup.com) | [@gman0922](https://github.com/gman0922), [Sheena Sharma](mailto:shesharma@expediagroup.com), [Alekhya Karuturi](mailto:akaruturi@expediagroup.com) | EG Developer Front Door |
| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go |
| [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling |
| [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. |
| [Lunar](https://lunar.app) | [Bjørn Hald Sørensen](https://github.com/crevil) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. |
| [Trendyol](https://trendyol.com) | [Gamze Senturk](https://github.com/gmzsenturk), [Mert Can Bilgic](https://github.com/mertcb) | The Developer Portal has been called `Pandora`. Provides an overview of Trendyol tech ecosystem. TechDocs, Catalog, Custom Plugins and Theme. |
| [Peloton](https://www.onepeloton.com/) | [Matt Waldron](https://github.com/daftgopher) | Creating our first developer portal and tech-docs. Exploring Service Catalog, Tech Insights and Cost Insights as well. |
| [Brex](https://www.brex.com/) | [Vamsi Chitters](https://github.com/vamsikc) | A centralized UI to understand how a service fits in the whole Brex architecture and manage a teams engineering dependencies. |
@@ -214,4 +214,5 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
| [Vipps](https://vipps.no) | [Martin Ehrnst](https://github.com/ehrnst) | Vipps use backstage for our service catalog, documentation, and developer portal. Using templates we are able to simplify the developer experience when deploying new services to our platform. |
| [Ferrovial](https://ferrovial.com) | [Jose Luis Rosado](mailto:jlrosado@ferrovial.com) | Backstage is helping us to improve and acelerate dev experience helping teams to quickly find technical documentation, infrastructure templates, pipelines, software components and quickstarters that have been developed by our squads in a inner source friendly environment. |
| [Inter&Co](https://bancointer.com.br) | [Arnaud Lanna](https://github.com/arnaudlanna), [Adriano Silva](https://github.com/adrianovss), [Bruno Grossi](https://github.com/begrossi) | We're using Backstage as our internal Developer Portal to catalog and collect repositories and microservices pieces of information like ownership, deployment time, and documentation. |
| [StatusNeo](https://statusneo.com/) | [Karan Nangru](mailto:nangru@statusneo.com), [@NishkarshRaj](https://github.com/NishkarshRaj), and [Gaurav Sarien](mailto:gaurav.sarien@statusneo.com) | Harnessing the power of central catalog inventory and self-serving software templates |
| [StatusNeo](https://statusneo.com/) | [Karan Nangru](mailto:nangru@statusneo.com), [@NishkarshRaj](https://github.com/NishkarshRaj), and [Gaurav Sarien](mailto:gaurav.sarien@statusneo.com) | Harnessing the power of central catalog inventory and self-serving software templates |
| [Alaska Airlines](https://alaskaair.com) | [@swerdick](https://github.com/swerdick) | Backstage is the developer portal for our 'software delivery platform'. Consolidating developer tools to one place, and providing automation to make it easy for developers to create and deploy applications to Kubernetes |
+1 -1
View File
@@ -53,7 +53,7 @@ Check out [the documentation](https://backstage.io/docs/getting-started) on how
- [Adopters](ADOPTERS.md) - Companies already using Backstage
- [Blog](https://backstage.io/blog/) - Announcements and updates
- [Newsletter](https://mailchi.mp/spotify/backstage-community) - Subscribe to our email newsletter
- [Backstage Community Sessions](https://github.com/backstage/community) - Join monthly meetup and explore Backstage community
- [Backstage Community Sessions](https://github.com/backstage/community) - Join monthly meetups and explore Backstage community
- Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️
## License
+317
View File
@@ -0,0 +1,317 @@
# Introduction
This file provides pointers for reviewing pull requests. While the main audience are reviewers, this can also be useful if you are contributing to this repository.
## Code Style
See our code style documented at [STYLE.md](./STYLE.md).
In particular when it comes to naming, make sure that naming follows established conventions within the project and/or package.
When adding new dependencies to packages it is always preferred to use version ranges that are already in use by other packages in the repository. This helps minimize lockfile changes and reduce package duplication, both in our repository as well as other Backstage installations.
## Secure Coding Practices
Be sure to familiarize yourself with our [secure coding practices](./SECURITY.md#coding-practices).
## Release & Versioning Policy
When reviewing pull requests it's important to consider our [versioning policy and release cycle](https://backstage.io/docs/overview/versioning-policy). Generally the most important bit is our [package versioning policy](https://backstage.io/docs/overview/versioning-policy#package-versioning-policy), which describes when and how we can ship breaking changes. We'll dive into how to identify breaking changes in a different section.
One other thing to keep in mind, especially when merging pull requests, is where in the release cycle we're currently at. In particular you want to avoid merging any large or risky changes towards the end of each release cycle. If there is a change that is ready to be merged, but you want to hold off until the next main line release, then you can label it with the `merge-after-release` label.
## Changesets
We use changesets to track changes in all published packages. Changesets both define what should go into the changelog of each package, but also what kind of version bump should be done for the next release.
An introduction to changesets can be found in our [contribution guidelines](./CONTRIBUTING.md#creating-changesets).
When reviewing a changeset, the most important things to look for are the bump levels, i.e. `major` / `minor` / `patch`, as well as whether the content is accurate and if it's written in a way that makes sense when reading it in the changelog for each package.
### Reviewing Changeset Bump Levels
The following table provides a reference for what type of version bump is needed for each package. This is applied separately to each individual package, it does not matter what the scope of a change is in any other broader context.
| Scope | Current Package Version | Bump Level |
| --------------- | ----------------------- | ---------- |
| Breaking Change | `1.0` and above | `major` |
| New Feature | `1.0` and above | `minor` |
| Fix | `1.0` and above | `patch` |
| Breaking Change | `0.x` | `minor` |
| New Feature | `0.x` | `patch` |
| Fix | `0.x` | `patch` |
The only situation where a package that is currently at `0.x` can have a `major` bump is if all owners and stakeholders of the package agree that the package is ready to be released as `1.0`.
### Reviewing Changeset Content
Each changeset should be written in a way that describes the impact of the change for the users of each package. The contents of the changesets will end up in the changelog of each package, for example [@backstage/core-plugin-api](./packages/core-plugin-api/CHANGELOG.md). The changelogs are intended to provide both a summary of the new features as well as guidance in the case of breaking changes or deprecations.
Some things that changeset should NOT contain are:
- Internal architecture details - these are generally not interesting to users, focus on the impact towards users of the package instead.
- Information related to a different package.
- A large amount of content, consider for example a separate migration guide instead, either in the package README or [./docs/](./docs/), and then link to that instead.
- Documentation - changesets can describe new features, but it should not be relied on for documenting them. Documentation should either be placed in [TSDoc](https://tsdoc.org) comments, package README, or [./docs/](./docs/).
### When is a changeset needed?
In general our changeset feedback bot will take care of informing whether a changeset is needed or not, but there are some edge cases. Whether a changeset is needed depends mostly on what files have been changed, but sometimes also on the kind of change that has been made.
Changes that do NOT need a new changeset:
- Changes to any test, storybook, or other local development files, for example, `MyComponent.test.tsx`, `MyComponent.stories.tsx`, `**mocks**/MyMock.ts`, `.eslintrc.js`, `setupTests.ts`, or `api-report.md`. Explained differently, it is only files that affect the published package that need changesets, such as source files and additional resources like `package.json`, `README.md`, `config.d.ts`, etc.
- When tweaking a change that has not yet been released, you can rely on and potentially modify the existing changeset instead.
- Changes that do not belong to a published packages, either because it's not a package at all, such as `docs/`, or because the package is private, such as `packages/app`.
- Changes that do not end up having an effect on the published package, such as whitespace fixes or code formatting changes. Although it's also fine to have a short changeset for these kind of changes too.
### Changeset Example
Consider the following scenario for a changeset:
A new `EntityList` component has been added to `plugins/catalog-react`.
Below are examples of a good and three bad changesets for that change.
**GOOD**
```md
---
'@backstage/plugin-catalog-react': minor
---
Added a new `EntityList` component that can be used to display detailed information about a list of entities.
```
The `@backstage/plugin-catalog-react` package has reached version `1.x`, which means that feature additions that aren't breaking should be a `minor` change. We don't bother with too much documentation, keeping it short and sweet. The main purpose is to inform users that this new component exists and to give them an idea of how they can use it.
**BAD**
```md
---
'@backstage/plugin-catalog-react': minor
'@backstage/plugin-catalog': minor
---
Added `EntityList` component.
Fixed a bug in the catalog index page.
```
This changeset is too short, it's best to give users an idea of how they can benefit from the new addition.
It also includes changes affecting both the Catalog and Catalog React library. It should be split into two separate changesets for each of the two packages, otherwise we'll end up with redundant and unrelated information in both changelogs.
**BAD**
```md
---
'@backstage/plugin-catalog-react': major
---
Added a new `EntityList` component that can be used to display detailed information about a list of entities. The component looks like this:
![EntityList screenshot](./docs/assets/headline.png)
It accepts the following properties:
- entities - The entities that should be listed.
- title - An optional formatting function for the list titles.
- dialog - An optional component that overrides the default details dialog.
```
This changeset is getting too detailed. It's not always bad to get this much into the weeds, but keep in mind that changesets are not easy to browse when searching for information about specific APIs. It's better to document things like this separately and keep the changeset more lean. Also avoid linking to assets in changesets, keep them text-only.
The change is also marked as a breaking `major` change. This should be changed to `minor` since adding new APIs is never a breaking change.
**BAD**
```md
---
'@backstage/plugin-catalog-react': patch
---
Added a new `EntityList` component that can be used to display detailed information about a list of entities. The `ListView` component was also refactored in order to make it possible to reuse it between the new `EntityList` and `KindList` components.
```
Assuming that the `ListView` component is not public API, this changeset goes into details that are not interesting to the user of the package. Internal changes do not need to be highlighted in changesets. If an internal refactor is the only change then it's alright to say something short like "Internal refactor to improve code reuse", but otherwise those details should be left out.
The `@backstage/plugin-catalog-react` package has also reached `1.x`, which means that new features should be introduced through a `minor` bump. We'd only use `patch` bumps for minor changes or fixes that do not affect the public API.
## Breaking Changes
Identifying breaking changes can be quite tricky. You need to look at the changes from both the point of view of consumers and producers of APIs, as well as behavioral changes. In this section we explore a couple of methods for identifying whether a change is breaking or not.
### Behavioral Changes
These are changes where the behavior of the code changes, but the public API is unchanged or doesn't have any breaking changes. They can be anything from tiny tweaks, like adding a bit of padding to a visual element, to a complete redesign and refactor of an entire plugin.
It's hard to set up exact rules for when a behavioral change is breaking or not. In some cases it's obvious, for example if you remove important functionality of a system, while in other cases it can be very hard to tell. In the end what's important is whether a significant number of users of the package will be negatively impacted by the change. One question that you can ask yourself here is "is it likely that there are users that don't want the new behavior, or will need to change their code to adapt to the new behavior?" If the answer is yes, then it's likely a breaking change. You do also want to keep [xkcd.com/1172](https://xkcd.com/1172/) in mind though.
Note that even a bug fix can be considered a breaking change in some situations. One thing to lean on in that case is what the _documented_ behavior is. If the current behavior does not match the documented behavior, then a change to match the documentation is generally not a breaking change. That is unless it is likely that there are a significant number of users that will be impacted by the change.
For tricky behavioral changes you may simply need to let end users provide feedback. This can be done either by hiding the new behavior behind an experimental feature switch, or by releasing the change early on in the release cycle, preferably in the first or second `next`-line release. Be ready to respond to feedback and potentially revert the change if needed.
### Public API Changes
Typescript is a huge help when it comes to identifying breaking changes, as well as the API Reports that we generate for all packages. Most of the time it is enough to only look at the API Reports to determine whether a change is breaking or not. If you determine that a change is breaking at the TypeScript level, then it is a breaking change.
In this section we will be talking about changed "types", but by that we mean any kind of exported symbol from packages, such as TypeScript types aliases or interfaces, functions, classes, constants, etc.
#### API Reports
We generate API Reports using the [API Extractor](https://api-extractor.com/) tool. These reports are generated for most packages in the Backstage repository, and are stored in the `api-report.md` file of each package. For CLI package we use custom tooling, and instead store the result in `cli-report.md`. Whenever the public API of a package changes, the API Report needs to be updated to reflect the new state of the API. Our CI checks will fail if the API reports are not up to date in a pull request.
Each API report contains a list of all the exported types of each package. As long as the API report does not have any warnings it will contain the full publicly facing API of the package, meaning you do not need to consider any other changes to the package from the point of view of TypeScript API stability.
Exported types can be marked with either `@public`, `@alpha` or `@beta` release tags. It is only the `@public` exports that we consider to be part of the stable API. The `@alpha` and `@beta` exports are considered unstable and can be changed at any time without needing a breaking package versions bump. However, this **ONLY** applies if the package has been configured to use experimental type builds, which looks like this in `package.json`:
```json
"build": "backstage-cli package build --experimental-type-build"
```
If a package does not have this configuration, then all exported types are considered stable, even if they are marked as `@alpha` or `@beta`.
#### Type Contract Direction
An important distinction to make when looking at changes to an API Report is the direction of the contract of a changed type, that is, whether it's used as input or output from the user's point of view. In the next two sections we'll dive into the different directions of a type contract, and how it affects whether a change is breaking or not.
#### Input Types
An input type is one where a value needs to be provided by users of the package. The most common form of input type are function, constructor, and method parameters.
The following is an example where `MyComponentProps` is an input type:
```ts
type MyComponentProps = {
title: string;
size?: 'small' | 'medium' | 'large';
};
function MyComponent(props: MyComponentProps): JSX.Element;
```
And from the package user's point of view it would look something like this:
```tsx
<MyComponent title="Hello World" size="medium" />
```
When modifying an input type, any change that increases constraints are breaking. For example, if we made the `size` prop required, that would be a breaking change. Likewise, if we changed the type of `size` to `'small' | 'large'`, that would also be breaking.
On the other hand, it's fine to relax constraints without it being a breaking change. For example, if we made the `title` prop optional, that would not be breaking. Likewise, if we changed the type of `size` to `'small' | 'medium' | 'large' | 'huge'`, that would not be breaking either. It is also possible to add new properties without it being a breaking change, as long as they are optional.
There's an edge-case where completely removing a property is also considered a breaking change. That's because of TypeScript being strict and refusing unknown properties, rather than a runtime breaking change. It is typically an easy thing for consumers to fix though.
Another way to think about the rules for evolving input types is that the old type must be assignable to the new type. In this case for example `_props: NewComponentProps = {} as OldComponentProps`. It's not a silver bullet though, because of edge-cases like the one mentioned above.
#### Output Types
An output type is one that the user receives from the packages. One of the most obvious examples here are the top-level exports from the package itself, but it also includes for example function return types.
The following is an example where both `useBox` and `Box` are output types:
```ts
type Box = {
title: string;
shape?: 'square' | 'rounded';
};
function useBox(): Box;
```
And from the consumer's point of view it would look something like this:
```ts
const { title, shape } = useBox();
```
When modifying an output type, any change that reduces constraints are breaking. For example, if we made the `title` property optional, that would be a breaking change, or if we changed the type of `shape` to `'square' | 'rounded' | 'octagon'`.
Adding new properties is not a breaking change, regardless of whether they are optional or not. Removing properties is on the other hand always breaking.
It is generally fine to increase constraints without it being a breaking change. For example, if we made the `shape` property required, that would not be breaking.
There are some edge-cases though, for example if `shape` was changed to just `'square'`, that would be a breaking change because consumers might be checking for `box.shape === 'rounded'`, which would then be breaking. It's typically a quite easy thing for consumers to fix though. More generally, type unions and discriminated unions are quite troublesome in output types, as both adding and removing types from them are considered breaking changes.
Another way to think about the rules for evolving output types is that the new type must be assignable to the old type. In this case for example `_box: OldBox = {} as NewBox`. It's not a silver bullet though, because of edge-cases like the one mentioned above.
#### I/O Types
Some types are considered both input and output types. For example, consider the following example:
```ts
type Point = {
x: number;
y: number;
};
function trimCoords(point: Point): Point;
```
In this case `Point` is both an input and output type. This means that the only changes we can make to the type that aren't breaking are the intersection of allowed changes between input and output types. In practice this only allows for the addition of new optional properties. Because of this constraint it is generally best to avoid using I/O types, and keep the input separated from the output.
There are some cases where I/O types favor either input or output when it comes to API stability. For example, all types used by Utility APIs are I/O types, but the stability of the output is a lot more important than the stability of the input. That is because it's a lot easier for the single producer of the input interface to adapt to changes compared to all consumers of the API that use it as an output type.
#### Identifying the Contract Direction
The only way to identify the contract direction of a type is to look at the context in which it's being used. In particular this can be tricky when looking at individual type aliases and interfaces, as you need to look at the rest of the package exports to see how the type is being used.
One important rule is that the context considered for any type is limited to only the package in which the type is declared. Just because a type is imported in a different package and used as an input type does not make it an input type.
The following rules can be used to identify the direction of a type alias or interface:
- If the type is used in an input context, for example function parameter, then it's an input type.
- If the type is used in an output context, for example function return type, then it's an output type.
- If the type is referenced by another type, then it inherits the direction of that type, except if referenced through a function callback, in which case the direction is reversed.
- If the type is used or inherits both input and output contexts, then it's an I/O type.
- If the type is not referenced anywhere else, then it's an I/O type.
Below is an example of the public API of a package, with type directions assigned to each export:
```ts
// I/O, used by getPoint as return type and referenced by BoxProps, an input type
interface Point {
x: number;
y: number;
}
// Output, since it's an exported function
function getPoint(): Point;
// Input, used by Box as parameter type
interface BoxProps {
point?: Point
}
// Output, since it's an exported function
function Box(props: BoxProps): JSX.Element;
// Output, used by createWidget as return type
interface Widget {
...
}
// Output, as it's referenced by WidgetOptions, which is an input
// type, but the render callback causes a direction reversal
interface WidgetProps {
...
}
// Input, just like WidgetProps this is due to the direction reversal
// caused by the render callback
type RenderedWidget = JSX.Element | null;
// Input, used by createWidget parameter type
interface WidgetOptions {
render(props: WidgetProps): RenderedWidget;
}
// Output, since it's an exported function
function createWidget(options: WidgetOptions): Widget;
// I/O, since it's not referenced anywhere else
type LabelStyle = 'normal' | 'thin';
// Output, since it's an exported constant
const LABEL_SIZE: number;
```
+26 -2
View File
@@ -11,8 +11,8 @@ Our TypeScript style is inspired by the [style guidelines](https://github.com/Mi
1. Use PascalCase for type names.
1. Do not use `I` as a prefix for interface names.
1. Use PascalCase for `enum` values.
1. Use camelCase for function names.
1. Use camelCase for property names and local variables.
1. Use `camelCase` for function names.
1. Use `camelCase` for property names and local variables.
1. Do not use `_` as a prefix for private properties.
1. Use whole words in names when possible.
1. Give type parameters names prefixed with `T`, for example `Request<TBody>`.
@@ -134,6 +134,30 @@ This section describes guidelines for designing public APIs. It can also be appl
}
```
1. When there is a significant number of arguments to a function or method, prefer to use a single options object as the argument, rather than many positional arguments.
```ts
// Bad
function createWidget(id: string, name: string, width: number) {}
// Good
function createWidget(options: CreateWidgetOptions) {}
```
1. Avoid arrays as return types; prefer response objects.
```ts
interface UserApi {
// Bad
// Can only return Users without signaling additional information such as pagination.
listUsers(): Promise<User[]>;
// Good
// Easy to evolve with additional fields.
listUsers(): Promise<ListUsersResponse>;
}
```
# Documentation Guidelines
We use [API Extractor](https://api-extractor.com/pages/overview/demo_docs/) to generate our documentation, which in turn uses [TSDoc](https://github.com/microsoft/tsdoc) to parse our doc comments.
+2 -1
View File
@@ -216,7 +216,6 @@ catalog:
- Component
- API
- Resource
- Template
- System
- Domain
- Location
@@ -271,6 +270,8 @@ catalog:
# Backstage example templates
- type: file
target: ../../plugins/scaffolder-backend/sample-templates/all-templates.yaml
rules:
- allow: [Template]
# Backstage end-to-end tests of TechDocs
- type: file
target: ../../cypress/e2e-fixture.catalog.info.yaml
@@ -75,7 +75,7 @@ export const createAuthMiddleware = async (
// Authorization header may be forwarded by plugin requests
req.headers.authorization = `Bearer ${token}`;
}
if (token && token !== req.cookies.token) {
if (token && token !== req.cookies?.token) {
setTokenCookie(res, {
token,
secure,
@@ -106,3 +106,7 @@ There are some custom metrics that have been added to Backstage will be output f
- `catalog_processing_duration_seconds`: Time spent executing the full processing flow
- `catalog_processors_duration_seconds`: Time spent executing catalog processors
- `catalog_processing_queue_delay_seconds`: The amount of delay between being scheduled for processing, and the start of actually being processed
- `scaffolder_task_count`: Tracks successful task runs.
- `scaffolder_task_duration`: a histogram which tracks the duration of a task run
- `scaffolder_step_count`: a count that tracks each step run
- `scaffolder_step_duration`: a histogram which tracks the duration of each step run
+3 -3
View File
@@ -414,8 +414,8 @@ __metadata:
linkType: hard
"cypress@npm:^10.0.0":
version: 10.8.0
resolution: "cypress@npm:10.8.0"
version: 10.10.0
resolution: "cypress@npm:10.10.0"
dependencies:
"@cypress/request": ^2.88.10
"@cypress/xvfb": ^1.2.4
@@ -461,7 +461,7 @@ __metadata:
yauzl: ^2.10.0
bin:
cypress: bin/cypress
checksum: c052690049980e7721e6fca563b724fde839d87d83c1478dfe26ce7d230992717c2c4028e7157bfb39ec274473e51929f49e5aab6a23c2b25cde2a439b1c3cf9
checksum: 668a32534a527dba79754abbf98af176b80c539a12ec00058932ba2a19c794c7888323e59e738c30f726ad740c5451c31d02548a0cb7c1b1c8ad01c55a984ca2
languageName: node
linkType: hard
-7
View File
@@ -1,7 +0,0 @@
---
id: backend
title: Backend
description: About Backend
---
## TODO
+54 -54
View File
@@ -13,31 +13,31 @@ both with other plugins and the app itself.
Backstage provides two primary methods for plugins to communicate across their
boundaries in client-side code. The first one being the
[createPlugin](../reference/core-plugin-api.createplugin.md) API along with the
[`createPlugin`](../reference/core-plugin-api.createplugin.md) API along with the
extensions that it can provide, and the second one being Utility APIs. While the
[createPlugin](../reference/core-plugin-api.createplugin.md) API is focused on
[`createPlugin`](../reference/core-plugin-api.createplugin.md) API is focused on
the initialization plugins and the app, the Utility APIs provide ways for
plugins to communicate during their entire life cycle.
## Consuming APIs
Each Utility API is tied to an [ApiRef](../reference/core-plugin-api.apiref.md)
Each Utility API is tied to an [`ApiRef`](../reference/core-plugin-api.apiref.md)
instance, which is a global singleton object without any additional state or
functionality, its only purpose is to reference Utility APIs.
[ApiRef](../reference/core-plugin-api.apiref.md)s are created using
[createApiRef](../reference/core-plugin-api.createapiref.md), which is exported
by [@backstage/core-plugin-api](../reference/core-plugin-api.md). There are also
[`ApiRef`](../reference/core-plugin-api.apiref.md)s are created using
[`createApiRef`](../reference/core-plugin-api.createapiref.md), which is exported
by [`@backstage/core-plugin-api`](../reference/core-plugin-api.md). There are also
many predefined Utility APIs in
[@backstage/core-plugin-api](../reference/core-plugin-api.md), and they're all
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md), and they're all
exported with a name of the pattern `*ApiRef`, for example
[errorApiRef](../reference/core-plugin-api.errorapiref.md).
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md).
To access one of the Utility APIs inside a React component, use the
[useApi](../reference/core-plugin-api.useapi.md) hook exported by
[@backstage/core-plugin-api](../reference/core-plugin-api.md), or the
[withApis](../reference/core-plugin-api.withapis.md) HOC if you prefer class
[`useApi`](../reference/core-plugin-api.useapi.md) hook exported by
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md), or the
[`withApis`](../reference/core-plugin-api.withapis.md) HOC if you prefer class
components. For example, the
[ErrorApi](../reference/core-plugin-api.errorapi.md) can be accessed like this:
[`ErrorApi`](../reference/core-plugin-api.errorapi.md) can be accessed like this:
```tsx
import React from 'react';
@@ -56,14 +56,14 @@ export const MyComponent = () => {
```
Note that there is no explicit type given for
[ErrorApi](../reference/core-plugin-api.errorapi.md). This is because the
[errorApiRef](../reference/core-plugin-api.errorapiref.md) has the type
embedded, and [useApi](../reference/core-plugin-api.useapi.md) is able to infer
[`ErrorApi`](../reference/core-plugin-api.errorapi.md). This is because the
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md) has the type
embedded, and [`useApi`](../reference/core-plugin-api.useapi.md) is able to infer
the type.
Also note that consuming Utility APIs is not limited to plugins, it can be done
from any component inside Backstage, including the ones in
[@backstage/core-plugin-api](../reference/core-plugin-api.md). The only
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md). The only
requirement is that they are beneath the `AppProvider` in the react tree.
## Supplying APIs
@@ -71,15 +71,15 @@ requirement is that they are beneath the `AppProvider` in the react tree.
### API Factories
APIs are registered in the form of
[ApiFactories](../reference/core-plugin-api.apifactory.md), which encapsulate
[`ApiFactory`](../reference/core-plugin-api.apifactory.md) instances, which encapsulate
the process of instantiating an API. It is a collection of three things: the
[ApiRef](../reference/core-plugin-api.apiref.md) of the API to instantiate, a
[`ApiRef`](../reference/core-plugin-api.apiref.md) of the API to instantiate, a
list of all required dependencies, and a factory function that returns a new API
instance.
For example, this is the default
[ApiFactory](../reference/core-plugin-api.apifactory.md) for the
[ErrorApi](../reference/core-plugin-api.errorapi.md):
[`ApiFactory`](../reference/core-plugin-api.apifactory.md) for the
[`ErrorApi`](../reference/core-plugin-api.errorapi.md):
```ts
createApiFactory({
@@ -93,25 +93,25 @@ createApiFactory({
});
```
In this example the [errorApiRef](../reference/core-plugin-api.errorapiref.md)
In this example the [`errorApiRef`](../reference/core-plugin-api.errorapiref.md)
is our API, which encapsulates the
[ErrorApi](../reference/core-plugin-api.errorapi.md) type. The
[alertApiRef](../reference/core-plugin-api.alertapiref.md) is our single
[`ErrorApi`](../reference/core-plugin-api.errorapi.md) type. The
[`alertApiRef`](../reference/core-plugin-api.alertapiref.md) is our single
dependency, which we give the name `alertApi`, and is then passed on to the
factory function, which returns an implementation of the
[ErrorApi](../reference/core-plugin-api.errorapi.md).
[`ErrorApi`](../reference/core-plugin-api.errorapi.md).
The [createApiFactory](../reference/core-plugin-api.createapifactory.md)
The [`createApiFactory`](../reference/core-plugin-api.createapifactory.md)
function is a thin wrapper that enables TypeScript type inference. You may
notice that there are no type annotations in the above example, and that is
because we're able to infer all types from the
[ApiRef](../reference/core-plugin-api.apiref.md)s. TypeScript will make sure
[`ApiRef`](../reference/core-plugin-api.apiref.md)s. TypeScript will make sure
that the return value of the `factory` function matches the type embedded in
`api`'s [ApiRef](../reference/core-plugin-api.apiref.md), in this case the
[ErrorApi](../reference/core-plugin-api.errorapi.md). It will also match the
`api`'s [`ApiRef`](../reference/core-plugin-api.apiref.md), in this case the
[`ErrorApi`](../reference/core-plugin-api.errorapi.md). It will also match the
types between the `deps` and the parameters of the `factory` function, again
using the type embedded within the
[ApiRef](../reference/core-plugin-api.apiref.md)s.
[`ApiRef`](../reference/core-plugin-api.apiref.md)s.
## Registering API Factories
@@ -123,13 +123,13 @@ app, and the app itself.
Starting with the Backstage core library, it provides implementations for all of
the core APIs. The core APIs are the ones exported by
[@backstage/core-plugin-api](../reference/core-plugin-api.md), such as the
[errorApiRef](../reference/core-plugin-api.errorapiref.md) and
[configApiRef](../reference/core-plugin-api.configapiref.md).
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md), such as the
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md) and
[`configApiRef`](../reference/core-plugin-api.configapiref.md).
The core APIs are loaded for any app created with
[createApp](../reference/app-defaults.createapp.md) from
[@backstage/core-plugin-api](../reference/app-defaults.md), which means that
[`createApp`](../reference/app-defaults.createapp.md) from
[`@backstage/core-plugin-api`](../reference/app-defaults.md), which means that
there is no step that needs to be taken to include these APIs in an app.
### Plugin APIs
@@ -137,13 +137,13 @@ there is no step that needs to be taken to include these APIs in an app.
In addition to the core APIs, plugins can define and export their own APIs.
While doing so they should usually also provide default implementations of their
own APIs, for example, the `catalog` plugin exports `catalogApiRef`, and also
supplies a default [ApiFactory](../reference/core-plugin-api.apifactory.md) of
supplies a default [`ApiFactory`](../reference/core-plugin-api.apifactory.md) of
that API using the `CatalogClient`. There is one restriction to plugin-provided
API Factories: plugins may not supply factories for core APIs, trying to do so
will cause the app to refuse to start.
Plugins supply their APIs through the `apis` option of
[createPlugin](../reference/core-plugin-api.createplugin.md), for example:
[`createPlugin`](../reference/core-plugin-api.createplugin.md), for example:
```ts
export const techdocsPlugin = createPlugin({
@@ -168,7 +168,7 @@ Lastly, the app itself is the final point where APIs can be added, and what has
the final say in what APIs will be loaded at runtime. The app may override the
factories for any of the core or plugin APIs, with the exception of the config,
app theme, and identity APIs. These are static APIs that are tied into the
[createApp](../reference/app-defaults.createapp.md) implementation, and
[`createApp`](../reference/app-defaults.createapp.md) implementation, and
therefore not possible to override.
Overriding APIs is useful for apps that want to switch out behavior to tailor it
@@ -231,19 +231,19 @@ const app = createApp({
```
Note that the above line will cause an error if `IgnoreErrorApi` does not fully
implement the [ErrorApi](../reference/core-plugin-api.errorapi.md), as it is
implement the [`ErrorApi`](../reference/core-plugin-api.errorapi.md), as it is
checked by the type embedded in the
[errorApiRef](../reference/core-plugin-api.errorapiref.md) at compile time.
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md) at compile time.
## Defining custom Utility APIs
Plugins are free to define their own Utility APIs. Simply define the TypeScript
interface for the API, and create an
[ApiRef](../reference/core-plugin-api.apiref.md) using
[createApiRef](../reference/core-plugin-api.createapiref.md) exported from
[@backstage/core-plugin-api](../reference/core-plugin-api.md). Also be sure to
[`ApiRef`](../reference/core-plugin-api.apiref.md) using
[`createApiRef`](../reference/core-plugin-api.createapiref.md) exported from
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md). Also be sure to
provide at least one implementation of the API, and to declare a default factory
for the API in [createPlugin](../reference/core-plugin-api.createplugin.md).
for the API in [`createPlugin`](../reference/core-plugin-api.createplugin.md).
Custom Utility APIs can be either public or private, which is up to the plugin
to choose. Private APIs do not expose an external API surface, and it's
@@ -255,16 +255,16 @@ backwards compatibility of public APIs, as you may otherwise break apps that are
using your plugin.
To make an API public, simply export the
[ApiRef](../reference/core-plugin-api.apiref.md) of the API, and any associated
[`ApiRef`](../reference/core-plugin-api.apiref.md) of the API, and any associated
types. To make an API private, just avoid exporting the
[ApiRef](../reference/core-plugin-api.apiref.md), but still be sure to supply a
default factory to [createPlugin](../reference/core-plugin-api.createplugin.md).
[`ApiRef`](../reference/core-plugin-api.apiref.md), but still be sure to supply a
default factory to [`createPlugin`](../reference/core-plugin-api.createplugin.md).
Private APIs are useful for plugins that want to depend on other APIs outside of
React components, but not have to expose an entire API surface to maintain. When
using private APIs, it is fine to use the `typeof` of an implementing class as
the type parameter passed to
[createApiRef](../reference/core-plugin-api.createapiref.md), while public APIs
[`createApiRef`](../reference/core-plugin-api.createapiref.md), while public APIs
should always define a separate TypeScript interface type.
Plugins may depend on APIs from other plugins, both in React components and as
@@ -273,13 +273,13 @@ dependencies between plugins.
## Architecture
The [ApiRef](../reference/core-plugin-api.apiref.md) instances mentioned above
The [`ApiRef`](../reference/core-plugin-api.apiref.md) instances mentioned above
provide a point of indirection between consumers and producers of Utility APIs.
It allows for plugins and components to depend on APIs in a type-safe way,
without having a direct reference to a concrete implementation of the APIs. The
Apps are also given a lot of flexibility in what implementations to provide. As
long as they adhere to the contract established by an
[ApiRef](../reference/core-plugin-api.apiref.md), they are free to choose any
[`ApiRef`](../reference/core-plugin-api.apiref.md), they are free to choose any
implementation they want.
The figure below shows the relationship between
@@ -304,16 +304,16 @@ The indirection provided by Utility APIs also makes it straightforward to test
components that depend on APIs, and to provide a standard common development
environment for plugins. A proper test wrapper with mocked API implementations
is not yet ready, but it will be provided as a part of
[@backstage/test-utils](../reference/test-utils.md). It will provide mocked
[`@backstage/test-utils`](../reference/test-utils.md). It will provide mocked
variants of APIs, with additional methods for asserting a component's
interaction with the API.
The common development environment for plugins is included in
[@backstage/dev-utils](../reference/dev-utils.md), where the exported
[createDevApp](../reference/dev-utils.createdevapp.md) function creates an
[`@backstage/dev-utils`](../reference/dev-utils.md), where the exported
[`createDevApp`](../reference/dev-utils.createdevapp.md) function creates an
application with implementations for all core APIs already present. Contrary to
the method for wiring up Utility API implementations in an app created with
[createApp](../reference/app-defaults.createapp.md),
[createDevApp](../reference/dev-utils.createdevapp.md) uses automatic dependency
[`createApp`](../reference/app-defaults.createapp.md),
[`createDevApp`](../reference/dev-utils.createdevapp.md) uses automatic dependency
injection. This is to make it possible to replace any API implementation, and
having that be reflected in dependents of that API.
@@ -15,7 +15,7 @@ thing well". The module would be consumed
(`const localName = require('the-module');`) without having to know the internal
structure.
Now, ESModules are the primary authoring format. They have numerous benefits,
Now, `ESModules` are the primary authoring format. They have numerous benefits,
such as compile-time verification of exports, and standards-defined semantics.
They have a similar mechanism known as "default exports", which allows for a
consumer to `import localName from 'the-module';`. This is implicitly the same
+2 -2
View File
@@ -34,5 +34,5 @@ Records should be stored under the `architecture-decisions` directory.
## Superseding an ADR
If an ADR supersedes an older ADR then the older ADR's status is changed to
superseded by ADR-XXXX and links to the new ADR.
If an ADR supersedes an older ADR then the status of the older ADR is changed to
"superseded by ADR-XXXX", and links to the new ADR.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

+6 -1
View File
@@ -18,7 +18,7 @@ Settings for local development:
- Name: Backstage (or your custom app name)
- Redirect URI: `http://localhost:7007/api/auth/gitlab/handler/frame`
- Scopes: read_user
- Scopes: `read_user`
## Configuration
@@ -35,6 +35,8 @@ auth:
clientSecret: ${AUTH_GITLAB_CLIENT_SECRET}
## uncomment if using self-hosted GitLab
# audience: https://gitlab.company.com
## uncomment if using a custom redirect URI
# callbackUrl: https://${BASE_URL}/api/auth/gitlab/handler/frame
```
The GitLab provider is a structure with three configuration keys:
@@ -44,6 +46,9 @@ The GitLab provider is a structure with three configuration keys:
- `clientSecret`: The Application secret
- `audience` (optional): The base URL for the self-hosted GitLab instance, e.g.
`https://gitlab.company.com`
- `callbackUrl` (optional): The URL matching the Redirect URI registered when creating your GitLab OAuth App, e.g.
`https://$backstage.acme.corp/api/auth/gitlab/handler/frame`
Note: Due to a peculiarity with GitLab OAuth, ensure there is no trailing `/` after 'frame' in the URL.
## Adding the provider to the Backstage frontend
+6
View File
@@ -46,6 +46,12 @@ The Microsoft provider is a structure with three configuration keys:
- `clientSecret`: Secret, found on App Registration > Certificates & secrets
- `tenantId`: Directory (tenant) ID, found on App Registration > Overview
In order to finish signing a user in from Azure, the Backstage backend must
fetch their information from graph.microsoft.com (as seen in [this source
code](https://github.com/seanfisher/passport-microsoft/blob/0456aa9bce05579c18e77f51330176eb26373658/lib/strategy.js#L93-L95)),
so ensure that your Backstage backend has connectivity to this host.
Otherwise users may see an `Authentication failed, failed to fetch user profile` error when they attempt to log in.
## Adding the provider to the Backstage frontend
To add the provider to the frontend, add the `microsoftAuthApiRef` reference and
+1 -1
View File
@@ -138,7 +138,7 @@ may need to pass in all files using one or multiple `--config <path>` options.
> to change for different deployment environments should be static
> configuration, while it should otherwise be avoided.
When defining configuration for your plugin, keep keys camelCased and stick to
When defining configuration for your plugin, keep keys on `camelCase` form and stick to
existing casing conventions such as `baseUrl` rather than `baseURL`.
It is also usually best to prefer objects over arrays, as it makes it possible
+5 -5
View File
@@ -217,11 +217,11 @@ for some dashboards, such as GKE.
###### required parameters for GKE
| Name | Description |
| ----------- | ------------------------------------------------------------------------ |
| projectId | the ID of the GCP project containing your Kubernetes clusters |
| region | the region of GCP containing your Kubernetes clusters |
| clusterName | the name of your kubernetes cluster, within your `projectId` GCP project |
| Name | Description |
| ------------- | ------------------------------------------------------------------------ |
| `projectId` | the ID of the GCP project containing your Kubernetes clusters |
| `region` | the region of GCP containing your Kubernetes clusters |
| `clusterName` | the name of your kubernetes cluster, within your `projectId` GCP project |
Note that the GKE cluster locator can automatically provide the values for the
`dashboardApp` and `dashboardParameters` options if you set the
+105 -3
View File
@@ -111,7 +111,7 @@ in Backstage. While a default table experience, similar to the one provided by
the Catalog plugin, is made available for ease-of-use, it's possible for you to
provide a completely custom experience, tailored to the needs of your
organization. For example, TechDocs comes with an alternative grid based layout
(`<EntityListDocsGrid>`).
(`<EntityListDocsGrid>`) and panel layout (`TechDocsCustomHome`).
This is done in your `app` package. By default, you might see something like
this in your `App.tsx`:
@@ -126,18 +126,120 @@ const AppRoutes = () => {
};
```
### Using TechDocsCustomHome
You can easily customize the TechDocs home page using TechDocs panel layout
(`<TechDocsCustomHome />`).
Modify your `App.tsx` as follows:
```tsx
import { TechDocsCustomHome } from '@backstage/plugin-techdocs';
//...
const techDocsTabsConfig = [
{
label: "Recommended Documentation",
panels: [
{
title: 'Golden Path',
description: 'Documentation about standards to follow',
panelType: 'DocsCardGrid',
filterPredicate: entity => entity?.metadata?.tags?.includes('recommended') ?? false,
}
]
}
]
const AppRoutes = () => {
<FlatRoutes>
<Route path="/docs" element={<TechDocsCustomHome tabsConfig={techDocsTabsConfig} />}>
</FlatRoutes>;
};
```
### Building a Custom home page
But you can replace `<DefaultTechDocsHome />` with any React component, which
will be rendered in its place. Most likely, you would want to create and
maintain such a component in a new directory at
`packages/app/src/components/techdocs`, and import and use it in `App.tsx`:
For example, you can define the following Custom home page component:
```tsx
import React from 'react';
import { Content } from '@backstage/core-components';
import {
CatalogFilterLayout,
EntityOwnerPicker,
EntityTagPicker,
UserListPicker,
EntityListProvider,
} from '@backstage/plugin-catalog-react';
import {
TechDocsPageWrapper,
TechDocsPicker,
} from '@backstage/plugin-techdocs';
import { Entity } from '@backstage/catalog-model';
import {
EntityListDocsGrid,
DocsGroupConfig,
} from '@backstage/plugin-techdocs';
export type CustomTechDocsHomeProps = {
groups?: Array<{
title: React.ReactNode;
filterPredicate: (entity: Entity) => boolean;
}>;
};
export const CustomTechDocsHome = ({ groups }: CustomTechDocsHomeProps) => {
return (
<TechDocsPageWrapper>
<Content>
<EntityListProvider>
<CatalogFilterLayout>
<CatalogFilterLayout.Filters>
<TechDocsPicker />
<UserListPicker initialFilter="all" />
<EntityOwnerPicker />
<EntityTagPicker />
</CatalogFilterLayout.Filters>
<CatalogFilterLayout.Content>
<EntityListDocsGrid groups={groups} />
</CatalogFilterLayout.Content>
</CatalogFilterLayout>
</EntityListProvider>
</Content>
</TechDocsPageWrapper>
);
};
```
Then you can add the following to your `App.tsx`:
```tsx
import { CustomTechDocsHome } from './components/techdocs/CustomTechDocsHome';
// ...
const AppRoutes = () => {
<FlatRoutes>
<Route path="/docs" element={<TechDocsIndexPage />}>
<CustomTechDocsHome />
<CustomTechDocsHome
groups={[
{
title: 'Recommended Documentation',
filterPredicate: entity =>
entity?.metadata?.tags?.includes('recommended') ?? false,
},
{
title: 'My Docs',
filterPredicate: 'ownedByUser',
},
]}
/>
</Route>
</FlatRoutes>;
};
@@ -437,7 +539,7 @@ FROM python:3.8-alpine
RUN apk update && apk --no-cache add gcc musl-dev openjdk11-jdk curl graphviz ttf-dejavu fontconfig
RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==1.0.1
RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==1.1.7
RUN pip install mkdocs-kroki-plugin
+8 -8
View File
@@ -6,7 +6,7 @@ description: Documentation on Customizing look and feel of the App
Backstage ships with a default theme with a light and dark mode variant. The
themes are provided as a part of the
[@backstage/theme](https://www.npmjs.com/package/@backstage/theme) package,
[`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package,
which also includes utilities for customizing the default theme, or creating
completely new themes.
@@ -14,7 +14,7 @@ completely new themes.
The easiest way to create a new theme is to use the `createTheme` function
exported by the
[@backstage/theme](https://www.npmjs.com/package/@backstage/theme) package. You
[`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. You
can use it to override some basic parameters of the default theme such as the
color palette and font.
@@ -33,16 +33,16 @@ const myTheme = createTheme({
If you want more control over the theme, and for example customize font sizes
and margins, you can use the lower-level `createThemeOverrides` function
exported by [@backstage/theme](https://www.npmjs.com/package/@backstage/theme)
exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme)
in combination with
[createTheme](https://material-ui.com/customization/theming/#createmuitheme-options-args-theme)
from [@material-ui/core](https://www.npmjs.com/package/@material-ui/core). See
[`createTheme`](https://material-ui.com/customization/theming/#createmuitheme-options-args-theme)
from [`@material-ui/core`](https://www.npmjs.com/package/@material-ui/core). See
the "Overriding Backstage and Material UI css rules" section below.
You can also create a theme from scratch that matches the `BackstageTheme` type
exported by [@backstage/theme](https://www.npmjs.com/package/@backstage/theme).
exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
See the
[material-ui docs on theming](https://material-ui.com/customization/theming/)
[Material-UI docs on theming](https://material-ui.com/customization/theming/)
for more information about how that can be done.
## Using your Custom Theme
@@ -79,7 +79,7 @@ const app = createApp({
Note that your list of custom themes overrides the default themes. If you still
want to use the default themes, they are exported as `lightTheme` and
`darkTheme` from
[@backstage/theme](https://www.npmjs.com/package/@backstage/theme).
[`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
## Example of a custom theme
+10 -12
View File
@@ -284,27 +284,25 @@ otherwise something went terribly wrong.
## Create a new component using a software template
- Go to `create` and choose to create a website with the `React SSR Template`
- Type in a name, let's use `tutorial`
- Select the group `team-a` which will own this new website, and go to the next
step
- Go to `create` and choose to create a website with the `Example Node.js Template`
- Type in a name, let's use `tutorial` and click `Next Step`
<p align='center'>
<img src='../assets/getting-started/b-scaffold-1.png' alt='Software template deployment input screen asking for a name, the group owning this, and a description' />
<img src='../assets/getting-started/b-scaffold-1.png' alt='Software template deployment input screen asking for a name' />
</p>
- For the location, we're going to use the default
- As owner, type your GitHub username
- For the repository name, type `tutorial`. Go to the next step
- You should see the following screen:
<p align='center'>
<img src='../assets/getting-started/b-scaffold-2.png' alt='Software template deployment input screen asking for the GitHub username, and name of the new repo to create' />
</p>
- For host, it should default to github.com
- As owner, type your GitHub username
- For the repository name, type `tutorial`. Go to the next step
- Review the details of this new service, and press `Create` if you want to
deploy it like this.
- You can follow along with the progress, and as soon as every step is
finished, you can take a look at your new service
- You can follow along with the progress, and as soon as every step is
finished, you can take a look at your new service
Achievement unlocked. You've set up an installation of the core Backstage App,
made it persistent, and configured it so you are now able to use software
-174
View File
@@ -1,174 +0,0 @@
---
id: discovery
title: Bitbucket Discovery
sidebar_label: Discovery
# prettier-ignore
description: Automatically discovering catalog entities from repositories in Bitbucket
---
The Bitbucket integration has a special discovery processor for discovering
catalog entities located in Bitbucket. The processor will crawl your Bitbucket
account and register entities matching the configured path. This can be useful
as an alternative to static locations or manually adding things to the catalog.
## Installation
You will have to add the processor in the catalog initialization code of your
backend. The provider is not installed by default, therefore you have to add a
dependency to `@backstage/plugin-catalog-backend-module-bitbucket` to your backend
package.
```bash
# From your Backstage root directory
yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-bitbucket
```
And then add the processor to your catalog builder:
```diff
// In packages/backend/src/plugins/catalog.ts
+import { BitbucketDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-bitbucket';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);
+ builder.addProcessor(
+ BitbucketDiscoveryProcessor.fromConfig(env.config, { logger: env.logger })
+ );
```
## Self-hosted Bitbucket Server
To use the discovery processor with a self-hosted Bitbucket Server, you'll need
a Bitbucket integration [set up](../bitbucketServer/locations.md) with a `BITBUCKET_TOKEN` and a
`BITBUCKET_API_BASE_URL`. Then you can add a location target to the catalog
configuration:
```yaml
catalog:
locations:
- type: bitbucket-discovery
target: https://bitbucket.mycompany.com/projects/my-project/repos/service-*/catalog-info.yaml
```
Note the `bitbucket-discovery` type, as this is not a regular `url` processor.
The target is composed of four parts:
- The base instance URL, `https://bitbucket.mycompany.com` in this case
- The project key to scan, which accepts \* wildcard tokens. This can simply be
`*` to scan repositories from all projects. This example only scans for
repositories in the `my-project` project.
- The repository blob to scan, which accepts \* wildcard tokens. This can simply
be `*` to scan all repositories in the project. This example only looks for
repositories prefixed with `service-`.
- The path within each repository to find the catalog YAML file. This will
usually be `/catalog-info.yaml` or a similar variation for catalog files
stored in the root directory of each repository. If omitted, the default value
`catalog-info.yaml` will be used. E.g. given that `my-project`and `service-a`
exists, `https://bitbucket.mycompany.com/projects/my-project/repos/service-*/`
will result in:
`https://bitbucket.mycompany.com/projects/my-project/repos/service-a/catalog-info.yaml`.
## Bitbucket Cloud
To use the discovery processor with Bitbucket Cloud, you'll need a Bitbucket
integration [set up](../bitbucketCloud/locations.md) with a `username` and an `appPassword`. Then
you can add a location target to the catalog configuration:
```yaml
catalog:
locations:
- type: bitbucket-discovery
target: https://bitbucket.org/workspaces/my-workspace
```
Note the `bitbucket-discovery` type, as this is not a regular `url` processor.
The target is composed of the following parts:
- The base URL for Bitbucket, `https://bitbucket.org`
- The workspace name to scan (following the `workspaces/` path part), which must
match a workspace accessible with the username of your integration.
- (Optional) The project key to scan (following the `projects/` path part),
which accepts \* wildcard tokens. If omitted, repositories from all projects
in the workspace are included.
- (Optional) The repository blob to scan (following the `repos/` path part),
which accepts \* wildcard tokens. If omitted, all repositories in the
workspace are included.
- (Optional) The `catalogPath` query argument to specify the location within
each repository to find the catalog YAML file. This will usually be
`/catalog-info.yaml` or a similar variation for catalog files stored in the
root directory of each repository. If omitted, the default value
`catalog-info.yaml` will be used.
- (Optional) The `q` query argument to be passed through to Bitbucket for
filtering results via the API. This is the most flexible option and will
reduce the amount of API calls if you have a large workspace.
[See here for the specification](https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering)
for the query argument (will be passed as the `q` query parameter).
- (Optional) The `search=true` query argument to activate the mode utilizing code search.
- Is mutually exclusive to the `q` query argument.
- Allows providing values at `catalogPath` for finding catalog files as allowed by the `path` filter/modifier
[at Bitbucket Cloud's code search](https://confluence.atlassian.com/bitbucket/code-search-in-bitbucket-873876782.html#Search-Pathmodifier).
- `catalogPath=/catalog-info.yaml`
- `catalogPath=catalog-info.yaml` (anywhere in the repository)
- `catalogPath=/path/catalog-info.yaml`
- `catalogPath=path/catalog-info.yaml`
- `catalogPath=/path/*/catalog-info.yaml`
- `catalogPath=path/*/catalog-info.yaml`
- Supports multiple catalog files per repository depending on the `catalogPath` value.
- Registers `Location` entities for existing files only vs all matching repositories.
Examples:
- `https://bitbucket.org/workspaces/my-workspace/projects/my-project` will find
all repositories in the `my-project` project in the `my-workspace` workspace.
- `https://bitbucket.org/workspaces/my-workspace/repos/service-*` will find all
repositories starting with `service-` in the `my-workspace` workspace.
- `https://bitbucket.org/workspaces/my-workspace/projects/apis-*/repos/service-*`
will find all repositories starting with `service-`, in all projects starting
with `apis-` in the `my-workspace` workspace.
- `https://bitbucket.org/workspaces/my-workspace?q=project.key ~ "my-project"`
will find all repositories in a project containing `my-project` in its key.
- `https://bitbucket.org/workspaces/my-workspace?catalogPath=my/nested/path/catalog.yaml`
will find all repositories in the `my-workspace` workspace and use the catalog
file at `my/nested/path/catalog.yaml`.
- `https://bitbucket.org/workspaces/my-workspace?search=true&catalogPath=/catalog.yaml`
will find all `catalog.yaml` files located in the root of repositories in the workspace `my-workspace`.
- `https://bitbucket.org/workspaces/my-workspace?search=true&catalogPath=catalog.yaml`
will find all `catalog.yaml` files located anywhere within repositories in the workspace `my-workspace`.
- `https://bitbucket.org/workspaces/my-workspace?search=true&catalogPath=/my/nested/path/catalog.yaml`
will find all `catalog.yaml` files located within the directory `/my/nested/path/` within
repositories in the workspace `my-workspace`.
- `https://bitbucket.org/workspaces/my-workspace?search=true&catalogPath=my/nested/path/catalog.yaml`
will find all `catalog.yaml` files located within the directory `my/nested/path/` located anywhere within
repositories in the workspace `my-workspace`.
- `https://bitbucket.org/workspaces/my-workspace?search=true&catalogPath=/my/*/path/catalog.yaml`
will find all `catalog.yaml` files located within a directory `path/` located within any (recursive) directory
within the directory `my/` in the root of repositories in the workspace `my-workspace`
(`/my/nested/path/catalog.yaml`, `/my/very/nested/path/catalog.yaml`, ...).
- `https://bitbucket.org/workspaces/my-workspace/projects/apis-*/repos/service-*?search=true&catalogPath=catalog.yaml`
will find all `catalog.yaml` files located anywhere within repositories starting with `service-`
in projects starting with `api-` in the workspace `my-workspace`.
## Custom repository processing
The Bitbucket Discovery Processor will by default emit a location for each
matching repository for further processing by other processors. However, it is
possible to override this functionality and take full control of how each
matching repository is processed.
`BitbucketDiscoveryProcessor.fromConfig` takes an optional parameter
`options.parser` where you can set your own parser to be used for each matched
repository.
```typescript
const processor = BitbucketDiscoveryProcessor.fromConfig(env.config, {
parser: async function* customRepositoryParser({ client, repository }) {
// Custom logic for interpreting the matching repository.
// See defaultRepositoryParser for an example
},
logger: env.logger,
});
```
@@ -102,9 +102,3 @@ catalog:
- **`workspace`**:
Name of your organization account/workspace.
If you want to add multiple workspaces, you need to add one provider config each.
## Alternative
_Deprecated!_ Please raise issues for use cases not covered by the entity provider.
[You can use the `BitbucketDiscoveryProcessor`.](../bitbucket/discovery.md#bitbucket-cloud)
@@ -120,9 +120,3 @@ const provider = BitbucketServerEntityProvider.fromConfig(env.config, {
},
});
```
## Alternative
_Deprecated!_ Please raise issues for use cases not covered by the entity provider.
[You can use the `BitbucketDiscoveryProcessor`.](../bitbucket/discovery.md#self-hosted-bitbucket-server)
+1 -1
View File
@@ -38,7 +38,7 @@ a structure with up to six elements:
not set. The address used to clone a repo is the `cloneUrl` plus the repo name.
- `gitilesBaseUrl` (optional): This is needed for creating a valid user-friendly URL
that can be used for browsing the content of the provider. If not set a default
value will be created in the same way as the "baseUrl" option. There is no
value will be created in the same way as the `baseUrl` option. There is no
requirement to have Gitiles for the Backstage Gerrit integration but without it
some links in the Backstage UI will be broken.
- `username` (optional): The Gerrit username to use in API requests. If
+38
View File
@@ -0,0 +1,38 @@
---
id: locations
title: Gitea Locations
sidebar_label: Locations
description: Integrating source code stored in Gitea into the Backstage catalog
---
The Gitea integration supports loading catalog entities from a hosted repository. Entities can be added to
[static catalog configuration](../../features/software-catalog/configuration.md),
registered with the
[catalog-import](https://github.com/backstage/backstage/tree/master/plugins/catalog-import)
plugin.
## Configuration
To use this integration, add configuration to your root `app-config.yaml`:
```yaml
integrations:
gitea:
- host: gitea.example.com
password: ${GITEA_TOKEN}
- host: gitea.example.com
username: ${GITEA_USERNAME}
password: ${GITEA_PASSWORD}
```
Directly under the `gitea` key is a list of provider configurations, where you
can list the Gitea instances you want to be able to fetch
data from. Each entry is a structure with up to four elements:
- `host`: The host of the gitea instance that you want to match on.
- `baseUrl` (optional): Needed if the Gitea instance is not reachable at
the base of the `host` option (e.g. `https://git.company.com/gitea`). This is the address that you would open in a browser.
- `username` (optional): The gitea username to use in API requests.
- `password` (optional): The password or api token to authenticate with.
You may supply only the `password` field, if authenticating via API access tokens (generated in Settings > Applications).
+15 -1
View File
@@ -104,6 +104,13 @@ catalog:
topic:
include: ['backstage-include'] # optional array of strings
exclude: ['experiments'] # optional array of strings
validateLocationsExist:
organization: 'backstage' # string
catalogPath: '/catalog-info.yaml' # string
filters:
branch: 'main' # string
repository: '.*' # Regex
validateLocationsExist: true # optional boolean
enterpriseProviderId:
host: ghe.example.net
organization: 'backstage' # string
@@ -118,7 +125,8 @@ This provider supports multiple organizations via unique provider IDs.
- **`catalogPath`** _(optional)_:
Default: `/catalog-info.yaml`.
Path where to look for `catalog-info.yaml` files.
You can use wildcards - `*` or `**` - to search the path and/or the filename
You can use wildcards - `*` or `**` - to search the path and/or the filename.
Wildcards cannot be used if the `validateLocationsExist` option is set to `true`.
- **`filters`** _(optional)_:
- **`branch`** _(optional)_:
String used to filter results based on the branch name.
@@ -139,6 +147,12 @@ This provider supports multiple organizations via unique provider IDs.
- **`organization`**:
Name of your organization account/workspace.
If you want to add multiple organizations, you need to add one provider config each.
- **`validateLocationsExist`** _(optional)_:
Whether to validate locations that exist before emitting them.
This option avoids generating locations for catalog info files that do not exist in the source repository.
Defaults to `false`.
Due to limitations in the GitHub API's ability to query for repository objects, this option cannot be used in
conjunction with wildcards in the `catalogPath`.
- **`schedule`** _(optional)_:
- **`frequency`**:
How often you want the task to run. The system does its best to avoid overlapping invocations.
+1 -5
View File
@@ -16,17 +16,13 @@ integrations are used by many Backstage core features and other plugins.
Each key under `integrations` is a separate configuration for a single external
provider. Providers each have different configuration; here's an example of
configuration to use both GitHub and Bitbucket:
configuration to use GitHub:
```yaml
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
bitbucket:
- host: bitbucket.org
username: ${BITBUCKET_USERNAME}
appPassword: ${BITBUCKET_APP_PASSWORD}
```
See documentation for each type of integration for full details on
+1 -1
View File
@@ -11,7 +11,7 @@ For some use cases, you may want to define custom [rules](./concepts.md#resource
Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule in `packages/backend/src/plugins/permission.ts`, but you can put it anywhere that's accessible by your `backend` package.
```typescript
import type { Entity } from '@backstage/plugin-catalog-model';
import type { Entity } from '@backstage/catalog-model';
import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha';
import { createConditionFactory } from '@backstage/plugin-permission-node';
import { z } from 'zod';
-2
View File
@@ -53,7 +53,6 @@ $ yarn workspace backend add @backstage/plugin-permission-backend
2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything.
```typescript
import { IdentityClient } from '@backstage/plugin-auth-node';
import { createRouter } from '@backstage/plugin-permission-backend';
import {
AuthorizeResult,
@@ -119,7 +118,6 @@ permission:
2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission thats easy for us to test. This policy rejects any attempt to delete a catalog entity:
```diff
import { IdentityClient } from '@backstage/plugin-auth-node';
import { createRouter } from '@backstage/plugin-permission-backend';
import {
AuthorizeResult,
@@ -48,7 +48,9 @@ Edit `plugins/todo-list-backend/src/service/router.ts`:
...
- import { InputError } from '@backstage/errors';
- import { IdentityApi } from '@backstage/plugin-auth-node';
+ import { InputError, NotAllowedError } from '@backstage/errors';
+ import { getBearerTokenFromAuthorizationHeader, IdentityApi } from '@backstage/plugin-auth-node';
+ import { PermissionEvaluator, AuthorizeResult } from '@backstage/plugin-permission-common';
+ import { todoListCreatePermission } from '@internal/plugin-todo-list-common';
@@ -56,7 +58,7 @@ Edit `plugins/todo-list-backend/src/service/router.ts`:
export interface RouterOptions {
logger: Logger;
identity: IdentityClient;
identity: IdentityApi;
+ permissions: PermissionEvaluator;
}
@@ -69,11 +71,13 @@ Edit `plugins/todo-list-backend/src/service/router.ts`:
...
router.post('/todos', async (req, res) => {
const token = IdentityClient.getBearerToken(req.header('authorization'));
let author: string | undefined = undefined;
const user = token ? await identity.authenticate(token) : undefined;
const user = await identity.getIdentity({ request: req });
author = user?.identity.userEntityRef;
+ const token = getBearerTokenFromAuthorizationHeader(
+ req.header('authorization'),
+ );
+ const decision = (
+ await permissions.authorize([{ permission: todoListCreatePermission }], {
+ token,
@@ -128,10 +132,8 @@ In order to test the logic above, the integrators of your backstage instance nee
```diff
// packages/backend/src/plugins/permission.ts
- import { IdentityClient } from '@backstage/plugin-auth-node';
+ import {
+ BackstageIdentityResponse,
+ IdentityClient
+ } from '@backstage/plugin-auth-node';
import {
PermissionPolicy,
@@ -170,3 +172,117 @@ Let's flip the result back to `ALLOW` before moving on.
};
}
```
At this point everything is working but if you run `yarn tsc` you'll get some errors, let's fix those up.
First we'll clean up the `plugins/todo-list-backend/src/service/router.test.ts`:
```diff
import { getVoidLogger } from '@backstage/backend-common';
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
+ import { PermissionEvaluator } from '@backstage/plugin-permission-common';
import express from 'express';
import request from 'supertest';
import { createRouter } from './router';
+ const mockedAuthorize: jest.MockedFunction<PermissionEvaluator['authorize']> =
+ jest.fn();
+ const mockedPermissionQuery: jest.MockedFunction<
+ PermissionEvaluator['authorizeConditional']
+ > = jest.fn();
+ const permissionEvaluator: PermissionEvaluator = {
+ authorize: mockedAuthorize,
+ authorizeConditional: mockedPermissionQuery,
+ };
describe('createRouter', () => {
let app: express.Express;
beforeAll(async () => {
const router = await createRouter({
logger: getVoidLogger(),
identity: {} as DefaultIdentityClient,
+ permissions: toPermissionEvaluator,
});
app = express().use(router);
});
beforeEach(() => {
jest.resetAllMocks();
});
describe('GET /health', () => {
it('returns ok', async () => {
const response = await request(app).get('/health');
expect(response.status).toEqual(200);
expect(response.body).toEqual({ status: 'ok' });
});
});
});
```
Then we want to update the `plugins/todo-list-backend/src/service/standaloneServer.ts`, first we need to add the `@backstage/plugin-permission-node` package to `plugins/todo-list-backend/package.json` and then we can make the following edits:
```diff
import {
createServiceBuilder,
loadBackendConfig,
SingleHostDiscovery,
+ ServerTokenManager,
} from '@backstage/backend-common';
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
import { Server } from 'http';
import { Logger } from 'winston';
import { createRouter } from './router';
export interface ServerOptions {
port: number;
enableCors: boolean;
logger: Logger;
}
export async function startStandaloneServer(
options: ServerOptions,
): Promise<Server> {
const logger = options.logger.child({ service: 'todo-list-backend' });
logger.debug('Starting application server...');
const config = await loadBackendConfig({ logger, argv: process.argv });
const discovery = SingleHostDiscovery.fromConfig(config);
+ const tokenManager = ServerTokenManager.fromConfig(config, {
+ logger,
+ });
+ const permissions = ServerPermissionClient.fromConfig(config, {
+ discovery,
+ tokenManager,
+ });
const router = await createRouter({
logger,
identity: DefaultIdentityClient.create({
discovery,
issuer: await discovery.getExternalBaseUrl('auth'),
}),
+ permissions,
});
let service = createServiceBuilder(module)
.setPort(options.port)
.addRouter('/todo-list', router);
if (options.enableCors) {
service = service.enableCors({ origin: 'http://localhost:3000' });
}
return await service.start().catch(err => {
logger.error(err);
process.exit(1);
});
}
module.hot?.accept();
```
Now when you run `yarn tsc` you should have no more errors.
@@ -118,7 +118,7 @@ Providing a disabled state can be a helpful signal to users, but there may be ca
- <Grid item>
- <AddTodo onAdd={handleAdd} />
- </Grid>
+ <RequirePermission permission={todoListCreatePermission}>
+ <RequirePermission permission={todoListCreatePermission} errorPage={<></>}>
+ <Grid item>
+ <AddTodo onAdd={handleAdd} />
+ </Grid>
@@ -165,3 +165,25 @@ Providing a disabled state can be a helpful signal to users, but there may be ca
```
Now you should find that the component for adding a todo list item does not render at all. Success!
You can also use `RequirePermission` to prevent access to routes as well. Here's how that would look in your `packages/app/src/App.tsx`:
```diff
+ import { RequirePermission } from '@backstage/plugin-permission-react';
+ import { todoListCreatePermission } from '@internal/plugin-todo-list-common';
...
<Route path="/search" element={<SearchPage />}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage />} />
+ <Route path="/todo-list" element={
// You might want to create a "read" permission for this, we are just using this one as an example
+ <RequirePermission permission={todoListCreatePermission}>
+ <TodoListPage />
+ </RequirePermission>
</FlatRoutes>
```
Now if you try to navigate to `https://localhost:3000/todo-list` you'll get and error page if you do not have permission.
+1
View File
@@ -23,4 +23,5 @@ iconUrl: # Used as the src attribute for your logo.
# You can provide an external url or add your logo under static/img and provide a path
# relative to static/ e.g. img/my-logo.png
npmPackageName: # Your npm package name E.g. '@backstage/plugin-<etc>' quotes are required
addedDate: # The date plugin added to marketplace E.g. '2022-10-01' quotes are required
```
+12 -11
View File
@@ -52,12 +52,13 @@ learn how to contribute the integration yourself!
The following table summarizes events that, depending on the plugins you have
installed, may be captured.
| Action | Subject | Other Notes |
| ---------- | --------------------------------------------------- | ----------------------------------------------------------------- |
| `navigate` | The URL of the page that was navigated to | |
| `click` | The text of the link that was clicked on | The `to` attribute represents the URL clicked to |
| `search` | The search term entered in any search bar component | The `searchTypes` attribute holds `types` constraining the search |
| `discover` | The title of the search result that was clicked on | The `value` is the result rank. A `to` attribute is also provided |
| Action | Subject | Other Notes |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| `navigate` | The URL of the page that was navigated to | |
| `click` | The text of the link that was clicked on | The `to` attribute represents the URL clicked to |
| `create` | The `name` of the software being created; if no `name` property is requested by the given Software Template, then the string `new {templateName}` is used instead. | The context holds an `entityRef`, set to the template's ref (e.g. `template:default/template-name`) |
| `search` | The search term entered in any search bar component | The context holds `searchTypes`, representing `types` constraining the search |
| `discover` | The title of the search result that was clicked on | The `value` is the result rank. A `to` attribute is also provided |
If there is an event you'd like to see captured, please [open an
issue][add-event] describing the event you want to see and the questions it
@@ -301,11 +302,11 @@ it's important to keep each of these levels of detail disaggregated.
automatically as part of the `extension` in which the `filter` event was
captured).
- On the flip side, when adding `attributes` to an event, look at existing
events and see if the data you are capturing matches the intention, type, or
even the content of _their_ `attributes`. For instance, it may be common for
events that involve the Catalog to add details like entity `name`, `kind`,
and/or `namespace` as `attributes`. Using the same keys in your event will
- On the flip side, when adding `attributes` to or `context` around an event,
look at existing events and see if the data you are capturing matches the
intention, type, or even the content of _their_ `attributes` or `context`.
For instance, it's common for events that involve the Catalog to include an
`entityRef` contextual key. Using the same keys and values in your event will
ensure that events instrumented across plugins can easily be aggregated.
### Unit Testing Event Capture
+15 -15
View File
@@ -196,14 +196,14 @@ const App = () => (
There are a couple of naming patterns to adhere to as you build plugins, which
helps clarify the intent and usage of the exports.
| Description | Pattern | Examples |
| --------------------- | --------------- | ---------------------------------------------- |
| Top-level Pages | \*Page | CatalogIndexPage, SettingsPage, LighthousePage |
| Entity Tab Content | Entity\*Content | EntityJenkinsContent, EntityKubernetesContent |
| Entity Overview Card | Entity\*Card | EntitySentryCard, EntityPagerDutyCard |
| Entity Conditional | is\*Available | isPagerDutyAvailable, isJenkinsAvailable |
| Plugin Instance | \*Plugin | jenkinsPlugin, catalogPlugin |
| Utility API Reference | \*ApiRef | configApiRef, catalogApiRef |
| Description | Pattern | Examples |
| --------------------- | ----------------- | ---------------------------------------------------- |
| Top-level Pages | `\*Page` | `CatalogIndexPage`, `SettingsPage`, `LighthousePage` |
| Entity Tab Content | `Entity\*Content` | `EntityJenkinsContent`, `EntityKubernetesContent` |
| Entity Overview Card | `Entity\*Card` | `EntitySentryCard`, `EntityPagerDutyCard` |
| Entity Conditional | `is\*Available` | `isPagerDutyAvailable`, `isJenkinsAvailable` |
| Plugin Instance | `\*Plugin` | `jenkinsPlugin`, `catalogPlugin` |
| Utility API Reference | `\*ApiRef` | `configApiRef`, `catalogApiRef` |
### Routing System
@@ -515,10 +515,10 @@ deprecated while making the new additions, to then be removed at a later point.
Many export naming patterns have been changed to avoid import aliases and to
clarify intent. Refer to the following table to formulate the new name:
| Description | Existing Pattern | New Pattern | Examples |
| -------------------- | -------------------------- | --------------- | ---------------------------------------------- |
| Top-level Pages | Router | \*Page | CatalogIndexPage, SettingsPage, LighthousePage |
| Entity Tab Content | Router | Entity\*Content | EntityJenkinsContent, EntityKubernetesContent |
| Entity Overview Card | \*Card | Entity\*Card | EntitySentryCard, EntityPagerDutyCard |
| Entity Conditional | isPluginApplicableToEntity | is\*Available | isPagerDutyAvailable, isJenkinsAvailable |
| Plugin Instance | plugin | \*Plugin | jenkinsPlugin, catalogPlugin |
| Description | Existing Pattern | New Pattern | Examples |
| -------------------- | ---------------------------- | ----------------- | ---------------------------------------------------- |
| Top-level Pages | `Router` | `\*Page` | `CatalogIndexPage`, `SettingsPage`, `LighthousePage` |
| Entity Tab Content | `Router` | `Entity\*Content` | `EntityJenkinsContent`, `EntityKubernetesContent` |
| Entity Overview Card | `\*Card` | `Entity\*Card` | `EntitySentryCard`, `EntityPagerDutyCard` |
| Entity Conditional | `isPluginApplicableToEntity` | `is\*Available` | `isPagerDutyAvailable`, `isJenkinsAvailable` |
| Plugin Instance | `plugin` | `\*Plugin` | `jenkinsPlugin`, `catalogPlugin` |
+305
View File
@@ -0,0 +1,305 @@
---
id: new-backend-system
title: New Backend System
description: Details of the upcoming backend system
---
> **DISCLAIMER: The new backend system is under active development and is not considered stable**
## Status
The new backend system is under active development, and only a small number of plugins and services have been migrated so far. It is possible to try it out, but it is not recommended to use this new system in production yet.
You can find an example backend setup at https://github.com/backstage/backstage/tree/master/packages/backend-next.
## Overview
The new Backstage backend system is being built to help make it simpler to install backend plugins and keep projects up to date. It also changes the foundation to one that makes it a lot easier to evolve plugins and the system itself. You can read more about the reasoning in the [original RFC](https://github.com/backstage/backstage/issues/11611).
One of the goals of the new system was to reduce the code needed for setting up a Backstage backend and installing plugins. This is an example of how you create, add features, and start up your backend in the new system:
```ts
import { createBackend } from '@backstage/backend-defaults';
import { catalogPlugin } from '@backstage/plugin-catalog-backend';
// Create your backend instance
const backend = createBackend();
// Install all desired features
backend.add(catalogPlugin());
// Start up the backend
await backend.start();
```
One notable change that helped achieve this much slimmer backend setup is the introduction of dependency injection, with a system that is very similar to the one in the Backstage frontend.
## Building Blocks
This section introduces the high-level building blocks upon which this new system is built. These are all concepts that exist in our current system in one way or another, but the have all been lifted up to be first class concerns in the new system.
### Backend
This is the backend instance itself, which you can think of as the unit of deployment. It does not have any functionality in itself, but is simply responsible for wiring things together.
It is up to you to decide how many different backends you want to deploy. You can have all features in a single one, or split things out into multiple smaller deployments. All depending on your need to scale and isolate individual features.
### Plugins
Plugins provide the actual features, just like in our existing system. They operate completely independently of each other. If plugins what to communicate with each other, they must do so over the wire. There can be no direct communication between plugins through code. Because of this constraints, each plugins can be considered to be its own microservice.
### Services
Services provide utilities to help make it simpler to implement plugins, so that each plugin doesn't need to implement everything from scratch. There are both many built-in services, like the ones for logging, database access, and reading configuration, but you can also import third-party services, or create your own.
Services are also a customization point for individual backend installations. You can both override services with your own implementations, as well as make smaller customizations to existing services.
### Extension Points
Many plugins have ways in which you can extend them, for example entity providers for the Catalog, or custom actions for the Scaffolder. These extension patterns are now encoded into Extension Points.
Extension Points look a little bit like services, since you depended on them just like you would a service. A key difference is that extension points are registered and provided by plugins themselves, based on what customizations each individual plugin wants to expose.
Extension Points are also exported separately from the plugin instance itself, and a single plugin can also expose multiple different extension points at once. This makes it easier to evolve and deprecated individual Extension Points over time, rather than dealing with a single large API surface.
### Modules
Modules use the plugin Extension Points to add new features for plugins. They might for example add an individual Catalog Entity Provider, or one or more Scaffolder Actions. Modules are basically plugins for plugins.
Each module may only extend a single plugin, and the module must be deployed together with that plugin in the same backend instance. Modules may however only communicate with their plugin through its registered extension points.
Just like plugins, modules also have access to services and can depend on their own service implementations. They will however share services with the plugin that they extend, there are no module-specific service implementations.
## Creating Plugins
Plugins are created using the `createBackendPlugin` function. All plugins must have an ID and a register method. Plugins may also accept an options object, which can be either optional or required. The options are passed to the second parameter of the register method, and the options type is inferred and forwarded to the returned plugin factory function.
```ts
import {
configServiceRef,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
// export type ExamplePluginOptions = { exampleOption: boolean };
export const examplePlugin = createBackendPlugin({
// unique id for the plugin
id: 'example',
// It's possible to provide options to the plugin
// register(env, options: ExamplePluginOptions) {
register(env) {
env.registerInit({
deps: {
logger: loggerServiceRef,
},
// logger is provided by the backend based on the dependency on loggerServiceRef above.
async init({ logger }) {
logger.info('Hello from example plugin');
},
});
},
});
```
The plugin can then be installed in the backend using the returned plugin factory function:
```ts
backend.add(examplePlugin());
```
If we wanted our plugin to accept options as well, we'd accept the options as the second parameter of the register method:
```ts
export const examplePlugin = createBackendPlugin({
id: 'example',
register(env, options?: { silent?: boolean }) {
env.registerInit({
deps: { logger: loggerServiceRef },
async init({ logger }) {
if (!options?.silent) {
logger.info('Hello from example plugin');
}
},
});
},
});
```
Passing the option to the plugin during installation looks like this:
```ts
backend.add(examplePlugin({ silent: true }));
```
## Creating Modules
Some facts about modules
- A Module is able to extend a plugin with additional functionality using the `ExtensionPoint`s registered by the plugin.
- A module can only extend one plugin but can interact with multiple `ExtensionPoint`s registered by that plugin.
- A module is always initialized before the plugin it extends.
A module depends on the `ExtensionPoint`s exported by the target plugin's library package, for example `@backstage/plugin-catalog-node`, and does not directly declare a dependency on the plugin package itself.
Here's an example on how to create a module that adds a new processor using the `catalogProcessingExtensionPoint`:
```ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';
import { MyCustomProcessor } from './processor';
export const exampleCustomProcessorCatalogModule = createBackendModule({
moduleId: 'exampleCustomProcessor',
pluginId: 'catalog',
register(env) {
env.registerInit({
deps: {
catalog: catalogProcessingExtensionPoint,
},
async init({ catalog }) {
catalog.addProcessor(new MyCustomProcessor());
},
});
},
});
```
### Extension Points
Modules depend on extension points just as a regular dependency by specifying it in the `deps` section.
#### Defining an Extension Point
```ts
import { createExtensionPoint } from '@backstage/backend-plugin-api';
export interface ScaffolderActionsExtensionPoint {
addAction(action: ScaffolderAction): void;
}
export const scaffolderActionsExtensionPoint =
createExtensionPoint<ScaffolderActionsExtensionPoint>({
id: 'scaffolder.actions',
});
```
#### Registering an Extension Point
Extension points are registered by a plugin and extended by modules.
## Backend Services
The default backend provides several _services_ out of the box which includes access to configuration, logging, databases and more.
Service dependencies are declared using their `ServiceRef`s in the `deps` section of the plugin or module, and the implementations are then forwarded to the `init` method of the plugin or module.
### Service References
A `ServiceRef` is a named reference to an interface which are later used to resolve the concrete service implementation. Conceptually this is very similar to `ApiRef`s in the frontend.
Services is what provides common utilities that previously resided in the `PluginEnvironment` such as Config, Logging and Database.
On startup the backend will make sure that the services are initialized before being passed to the plugin/module that depend on them.
ServiceRefs contain a scope which is used to determine if the serviceFactory creating the service will create a new instance scoped per plugin/module or if it will be shared. `plugin` scoped services will be created once per plugin/module and `root` scoped services will be created once per backend instance.
#### Defining a Service
```ts
import {
createServiceFactory,
pluginMetadataServiceRef,
loggerServiceRef,
} from '@backstage/backend-plugin-api';
import { ExampleImpl } from './ExampleImpl';
export interface ExampleApi {
doSomething(): Promise<void>;
}
export const exampleServiceRef = createServiceRef<ExampleApi>({
id: 'example',
scope: 'plugin', // can be 'root' or 'plugin'
// The defaultFactory is optional to implement but it will be used if no other factory is provided to the backend.
// This is allows for the backend to provide a default implementation of the service without having to wire it beforehand.
defaultFactory: async service =>
createServiceFactory({
service,
deps: {
logger: loggerServiceRef,
plugin: pluginMetadataServiceRef,
},
// Logger is available directly in the factory as it's a root scoped service and will be created once per backend instance.
async factory({ logger }) {
// plugin is available as it's a plugin scoped service and will be created once per plugin.
return async ({ plugin }) => {
// This block will be executed once for every plugin that depends on this service
logger.info('Initializing example service plugin instance');
return new ExampleImpl({ logger, plugin });
};
},
}),
});
```
### Overriding Services
In this example we replace the default root logger service implementation with a custom one that streams logs to GCP. The `rootLoggerServiceRef` has a `'root'` scope, meaning there are no plugin-specific instances of this service.
```ts
import {
createServiceFactory,
rootLoggerServiceRef,
LoggerService,
} from '@backstage/backend-plugin-api';
// This custom implementation would typically live separately from
// the backend setup code, either nearby such as in
// packages/backend/src/services/logger/GoogleCloudLogger.ts
// Or you can let it live in its own library package.
class GoogleCloudLogger implements LoggerService {
static factory = createServiceFactory({
service: rootLoggerServiceRef,
deps: {},
async factory() {
return new GoogleCloudLogger();
},
});
// custom implementation here ...
}
// packages/backend/src/index.ts
const backend = createBackend({
services: [
// supplies additional or replacement services to the backend
GoogleCloudLogger.factory(),
],
});
```
## Testing
Utilities for testing backend plugins and modules are available in `@backstage/backend-test-utils`.
```ts
import { startTestBackend } from '@backstage/backend-test-utils';
describe('Example', () => {
it('should do something', async () => {
await startTestBackend({
// mock services can be provided to the backend
services: [someServiceFactory],
// plugins and modules for testing
features: [testModule()],
});
// assertions
});
});
```
## Package structure
A detailed explanation of the package architecture can be found in the [Backstage Architecture Overview](../overview/architecture-overview.md#package-architecture). The most important packages to consider for this system are `backend`, `plugin-<pluginId>-backend`, `plugin-<pluginId>-node`, and `plugin-<pluginId>-backend-module-<moduleId>`.
- `plugin-<pluginId>-backend` houses the implementation of the plugins themselves.
- `plugin-<pluginId>-node` houses the extension points and any other utilities that modules or other plugins might need.
- `plugin-<pluginId>-backend-module-<moduleId>` houses the modules that extend the plugins via the extension points.
- `backend` is the backend itself that wires everything together to something that you can deploy.
+1 -1
View File
@@ -83,7 +83,7 @@ export const ExamplePage = examplePlugin.provide(
This is where the plugin is created and where it creates and exports extensions
that can be imported and used the app. See reference docs for
[createPlugin](../reference/core-plugin-api.createplugin.md) or introduction to
[`createPlugin`](../reference/core-plugin-api.createplugin.md) or introduction to
the new [Composability System](./composability.md).
## Components
+4 -4
View File
@@ -174,14 +174,14 @@ which can be used to request the provider's API.
`read` then makes an authenticated request to the provider API and returns the
file's content.
#### readUrl
#### `readUrl`
`readUrl` is a new interface that allows complex response objects and is
intended to replace the `read` method. This new method is currently optional to
implement which allows for a soft migration to `readUrl` instead of `read` in
the future.
#### readTree
#### `readTree`
`readTree` method also expects user-friendly URLs similar to `read` but the URL
should point to a tree (could be the root of a repository or even a
@@ -241,8 +241,8 @@ without an `etag`, the response contains an ETag of the resource (should ideally
forward the ETag returned by the provider). If the method is called with an
`etag`, it first compares the ETag and returns a `NotModifiedError` in case the
resource has not been modified. This approach is very similar to the actual
[ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) and
[If-None-Match](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match)
[`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) and
[`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match)
HTTP headers.
### 6. Debugging
+1 -1
View File
@@ -38,7 +38,7 @@ Server-to-server authentication tokens issued from a TokenManager (specifically,
### Kubernetes
Added support for `oidc` as authProvider for kubernetes authentication and added an optional `oidcTokenProvider` config value. This will allow users to authenticate to kubernetes clusters using ID tokens obtained from the configured auth provider in their Backstage instance. Contributed by @dbravovmw(https://github.com/dbravovmw). #11328(https://github.com/backstage/backstage/pull/11328)
Added support for `oidc` as an auth provider for kubernetes authentication and added an optional `oidcTokenProvider` config value. This will allow users to authenticate to kubernetes clusters using ID tokens obtained from the configured auth provider in their Backstage instance. Contributed by @dbravovmw(https://github.com/dbravovmw). #11328(https://github.com/backstage/backstage/pull/11328)
### Misc
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -167,7 +167,7 @@ For more information about blog posts, click [here](https://docusaurus.io/docs/e
### Adding items to your site's top navigation bar
1. Add links to docs, custom pages or external links by editing the headerLinks field of `website/siteConfig.js`:
1. Add links to docs, custom pages or external links by editing the `headerLinks` field of `website/siteConfig.js`:
`website/siteConfig.js`
+1
View File
@@ -7,3 +7,4 @@ description: Browse your project's ADRs.
documentation: https://github.com/backstage/backstage/tree/master/plugins/adr
iconUrl: img/adr-logo.png
npmPackageName: '@backstage/plugin-adr'
addedDate: '2022-04-13'
+1
View File
@@ -7,3 +7,4 @@ description: Access Airbrake error monitoring and other integrations from within
documentation: https://github.com/backstage/backstage/blob/master/plugins/airbrake
iconUrl: https://wp-assets.airbrake.io/wp-content/uploads/2020/10/05222904/Square-white-A-on-Orange.png
npmPackageName: '@backstage/plugin-airbrake'
addedDate: '2022-01-10'
+1
View File
@@ -7,3 +7,4 @@ description: View Allure reports for your components in Backstage.
documentation: https://github.com/backstage/backstage/tree/master/plugins/allure
iconUrl: https://avatars.githubusercontent.com/u/5879127
npmPackageName: '@backstage/plugin-allure'
addedDate: '2021-09-02'
+1
View File
@@ -7,3 +7,4 @@ description: Components to discover and display API entities as an extension to
documentation: https://github.com/backstage/backstage/blob/master/plugins/api-docs/README.md
iconUrl: https://raw.githubusercontent.com/vscode-icons/vscode-icons/master/icons/file_type_swagger.svg
npmPackageName: '@backstage/plugin-api-docs'
addedDate: '2020-11-19'
@@ -7,3 +7,4 @@ description: Integrates Apollo Explorer graphs as a tool to browse GraphQL API e
documentation: https://github.com/backstage/backstage/blob/master/plugins/apollo-explorer/README.md
iconUrl: img/apollo-explorer.png
npmPackageName: '@backstage/plugin-apollo-explorer'
addedDate: '2022-07-20'
+1
View File
@@ -10,3 +10,4 @@ npmPackageName: '@roadiehq/backstage-plugin-argo-cd'
tags:
- cd
- ci
addedDate: '2021-04-20'

Some files were not shown because too many files have changed in this diff Show More