Merge branch 'master' into jenkins/jobrun-table

Signed-off-by: Abhay-soni-developer <abhaysoni.developer@gmail.com>
This commit is contained in:
Abhay-soni-developer
2023-09-14 19:17:02 +05:30
684 changed files with 13169 additions and 2496 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-proxy-backend': patch
---
Add the route name to an error message that appears when the backend
proxy wasn't well configured. This will help users to understand the
issue and fix the right configuration.
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/backend-tasks': patch
---
When starting a task that existed before, with a faster schedule than it
previously had, the task will now correctly obey the faster schedule
immediately. Before this fix, the new schedule was only obeyed after the next
pending (according to the old schedule) run had completed.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend-module-oauth2-provider': minor
---
New module for `@backstage/plugin-auth-backend` that adds a `oauth2` auth provider.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-kubernetes-backend': patch
---
Fixed a bug where requests to the proxy endpoint would fail for clusters with `caFile` configured
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Restored functionality to `OwnedEntityPicker` by converting deprecated `ui:options` input to `catalogFilter`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-newrelic': patch
---
Fixed bug in NewRelicComponent component where table would not sort correctly for numerical values.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Minor internal tweak to handle `classnames` update
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-import': minor
---
Slight change to the `PreparePullRequestFormProps`, because of an update to `react-hook-form`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-adr': patch
---
support for i18n feature
+47 -2
View File
@@ -1,5 +1,5 @@
{
"mode": "pre",
"mode": "exit",
"tag": "next",
"initialVersions": {
"example-app": "0.2.86",
@@ -241,46 +241,67 @@
"@backstage/frontend-plugin-api": "0.0.0",
"@backstage/plugin-auth-backend-module-gitlab-provider": "0.0.0",
"@backstage/backend-plugin-manager": "0.0.0",
"@backstage/plugin-permission-backend-module-allow-all-policy": "0.0.0"
"@backstage/plugin-permission-backend-module-allow-all-policy": "0.0.0",
"app-next-example-plugin": "0.0.0",
"@backstage/plugin-auth-backend-module-oauth2-provider": "0.0.0",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.0.0"
},
"changesets": [
"big-bananas-sleep",
"big-masks-divide",
"big-spies-agree",
"blue-rats-drop",
"brave-books-pretend",
"brave-carrots-occur",
"calm-cups-smile",
"catalog-everybody-to-the-limit",
"chatty-goats-mate",
"chatty-spiders-serve",
"chilly-balloons-enjoy",
"chilly-parrots-yell",
"clean-houses-invent",
"cold-apricots-poke",
"create-app-1692704146",
"create-app-1693311380",
"cuddly-avocados-warn",
"curvy-roses-march",
"cyan-roses-relate",
"dirty-boxes-give",
"dirty-carrots-notice",
"dirty-cars-poke",
"dirty-cars-smoke",
"dirty-garlics-kiss",
"dirty-years-travel",
"dry-wasps-occur",
"dull-eyes-pay",
"dull-tables-joke",
"early-rules-taste",
"early-trains.draw",
"eight-radios-listen",
"eight-timers-sin",
"eighty-emus-float",
"eighty-fans-provide",
"empty-rings-type",
"fair-paws-flash",
"few-nails-smile",
"fifty-adults-shake",
"forty-rocks-jog",
"four-masks-fry",
"fresh-candles-warn",
"friendly-pugs-lick",
"funny-ligers-matter",
"funny-timers-retire",
"giant-coats-brush",
"gorgeous-coins-attack",
"great-chairs-swim",
"grumpy-bikes-beg",
"grumpy-papayas-tie",
"grumpy-ties-know",
"healthy-spiders-arrive",
"healthy-tools-count",
"hip-seahorses-exist",
"honest-ads-rush",
"hungry-cherries-speak",
"hungry-dots-design",
"khaki-pigs-accept",
"khaki-spiders-sniff",
@@ -288,7 +309,10 @@
"late-hounds-yell",
"late-roses-dream",
"lazy-pumas-hope",
"lazy-pumas-shop",
"lazy-rice-rule",
"lemon-donuts-clap",
"light-drinks-rush",
"long-flowers-sort",
"loud-apricots-destroy",
"lovely-years-sniff",
@@ -296,28 +320,35 @@
"lucky-chicken-lie",
"many-tips-attend",
"mean-carpets-provide",
"mean-planes-smoke",
"mean-queens-act",
"mighty-crabs-cheat",
"modern-trees-add",
"nasty-cherries-exercise",
"nasty-forks-beg",
"nasty-snails-complain",
"nasty-worms-share",
"new-sheep-cover",
"nice-forks-remain",
"ninety-horses-remember",
"ninety-news-visit",
"old-geese-drive",
"orange-houses-fly",
"pink-cups-rescue",
"pink-months-hammer",
"plenty-lamps-pump",
"polite-forks-grin",
"polite-plums-share",
"poor-olives-own",
"poor-years-appear",
"popular-clocks-whisper",
"popular-coins-tap",
"popular-readers-allow",
"pretty-otters-whisper",
"proud-lamps-cross",
"red-pens-knock",
"renovate-2ebf420",
"renovate-36e7f58",
"renovate-76185f9",
"renovate-b129f45",
"renovate-bbda8e8",
@@ -325,27 +356,41 @@
"repo-tools-boo-descriptions-two-electric-boogaloo",
"repo-tools-boo-descriptions",
"repo-tools-ci-toldya-so",
"rotten-bees-wave",
"rude-dolphins-reply",
"short-planets-argue",
"silent-icons-deny",
"silent-numbers-retire",
"silly-emus-remain",
"six-radios-smile",
"six-wolves-admire",
"slow-ducks-hunt",
"slow-queens-visit",
"smart-islands-jog",
"stale-points-breathe",
"strange-frogs-count",
"stupid-ants-change",
"sweet-hairs-complain",
"sweet-pens-hunt",
"swift-frogs-drop",
"tall-countries-float",
"tall-items-clap",
"tall-peas-visit",
"tame-jokes-do",
"tender-garlics-report",
"thick-pets-drive",
"thin-dryers-repeat",
"three-hounds-wonder",
"tricky-melons-fold",
"twelve-elephants-pay",
"twenty-worms-enjoy",
"warm-llamas-bake",
"warm-panthers-lick",
"weak-tomatoes-dance",
"wet-donuts-fry",
"wise-tables-add",
"witty-ants-cover",
"witty-pans-flash",
"yellow-trains-protect",
"yellow-yaks-behave",
"young-ants-nail",
+11
View File
@@ -0,0 +1,11 @@
---
'@backstage/plugin-home-react': patch
'@backstage/plugin-home': patch
'@backstage/plugin-scaffolder-react': patch
'@backstage/plugin-scaffolder': patch
---
Updated dependency `@rjsf/utils` to `5.13.0`.
Updated dependency `@rjsf/core-v5` to `npm:@rjsf/core@5.13.0`.
Updated dependency `@rjsf/material-ui-v5` to `npm:@rjsf/material-ui@5.13.0`.
Updated dependency `@rjsf/validator-ajv8` to `5.13.0`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
When running `version:bump` it will now log duplicates instead of throwing an error
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-vault-backend': patch
'@backstage/plugin-vault': patch
---
Added ability to override vault secret engine value on catalog entity level using annotation `vault.io/secrets-engine`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-tech-radar': patch
---
Fixed `RadarTimeline` text formatting.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
Improved the `parseEntityRef` Scaffolder filter by introducing the ability for users to provide default kind and/or namespace values. The filter now takes
2 arguments, similarly to the original [parseEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/entity/ref.ts#L77).
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Removed the experimental `package fix` command that was used to automatically add dependencies to `package.json`, but has since been replaced by the `no-undeclared-imports` rule from `@backstage/eslint-plugin`.
+17
View File
@@ -0,0 +1,17 @@
---
'@backstage/plugin-catalog-backend-module-gitlab': minor
---
Fix Gitlab.com user ingestion by scoping GitlabOrgDiscoveryEntityProvider to a group.
**BREAKING** The `group` parameter is now required Gitlab.com Org Data integrations and the backend will fail to start without this option configured.
```diff
catalog:
providers:
gitlab:
yourProviderId:
host: gitlab.com
orgEnabled: true
+ group: org/teams
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': minor
---
adding a .zip download to dry run results page, including zip.js as dependency
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
**DEPRECATION**: Deprecated `ScaffolderEntitiesProcessor`, which should now instead be imported from `@backstage/plugin-catalog-backend-module-scaffolder-entity-model`.
`catalogModuleTemplateKind` was also moved to that package and renamed to `catalogModuleScaffolderEntityModel`, without any deprecation since it was an alpha export.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': minor
---
Added a dedicated module to collect the `ScaffolderEntitiesProcessor` and `catalogModuleTemplateKind`.
+2
View File
@@ -448,3 +448,5 @@ zoomable
zsh
Pulumi
Lightsail
PR
rebasing
+3
View File
@@ -98,6 +98,9 @@ jobs:
- name: verify api reference
run: node scripts/verify-api-reference.js
- name: verify catalog-info.yaml consistency
run: yarn backstage-repo-tools generate-catalog-info --ci
- name: lint openapi yaml files
run: yarn backstage-repo-tools schema openapi lint
+287 -31
View File
@@ -10,11 +10,83 @@ Backstage is released under the Apache 2.0 License, and original creations contr
You can find out more about the types of contributions over at [getting involved](https://backstage.io/docs/getting-started/getting-involved)!
If you need help, just jump into our [Discord chatroom](https://discord.gg/backstage-687207715902193673).
## Code of Conduct
This project adheres to the [CNCF Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code.
[code-of-conduct]: https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md
## Security Issues?
See [SECURITY](SECURITY.md).
## Accessibility
We encourage you to catch any accessibility issues already in the development phase of new features to Backstage, see our [Accessibility documentation](https://backstage.io/docs/accessibility/) for more details.
## Get Started!
So...feel ready to jump in? Let's do this. 👏🏻💯
Start by reading our [Getting Started for Contributors](https://backstage.io/docs/getting-started/contributors) page to get yourself setup with a fresh copy of Backstage ready for your contributions. If you need help, just jump into our [Discord chatroom](https://discord.gg/backstage-687207715902193673).
### Cloning the Repository
Ok. So you're gonna want some code right? Go ahead and fork the repository into your own GitHub account and clone that code to your local machine. GitHub's [Fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo) documentation has a great step by step guide if you are not sure how to do this.
If you cloned a fork, you can add the upstream dependency like so:
```bash
git remote add upstream git@github.com:backstage/backstage
git pull upstream master
```
After you have cloned the Backstage repository, you should run the following commands once to set things up for development:
```bash
cd backstage # change to root directory of project
yarn install # fetch dependency packages - may take a while
yarn tsc # does a first run of type generation and checks
```
### Serving the Example App
Open a terminal window and start the web app by using the following command from the project root. Make sure you have run the above mentioned commands first.
```bash
yarn dev
```
This is going to start two things, the frontend (:3000) and the backend (:7007).
This should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal.
By default, Backstage will start on port 3000, however you can override this by setting an environment variable `PORT` on your local machine. e.g. `export PORT=8080` then running `yarn start`. Or `PORT=8080 yarn start`.
Once successfully started, you should see the following message in your terminal window:
```sh
$ concurrently "yarn start" "yarn start-backend"
$ yarn workspace example-app start
$ yarn workspace example-backend start
$ backstage-cli app:serve
$ backstage-cli backend:dev
[0] Loaded config from app-config.yaml
[1] Build succeeded
[0] 「wds」: Project is running at http://localhost:3000/
[0] 「wds」: webpack output is served from /
[0] 「wds」: Content not from webpack is served from $BACKSTAGE_DIR/packages/app/public
[0] 「wds」: 404s will fallback to /index.html
[0] 「wdm」: wait until bundle finished: /
[1] 2021-02-12T20:58:17.614Z backstage info Loaded config from app-config.yaml
```
You'll see how you get both logs for the frontend `webpack-dev-server` which serves the react app ([0]) and the backend ([1]);
Visit <http://localhost:3000> and you should see the bleeding edge of Backstage ready for contributions!
If you want to get a better understanding of the layout of the repo not that you have a local copy running feel free to review the [Backstage Project Structure](https://backstage.io/docs/getting-started/project-structure) documentation.
## Coding Guidelines
@@ -28,6 +100,163 @@ Also be sure to skim through our [ADRs](docs/architecture-decisions) to see if t
If there are any updates in `markdown` file please make sure to run `yarn run lint:docs`. Though it is checked on `lint-staged`. It is required to install [vale](https://docs.errata.ai/vale/install) separately and make sure it is accessed by global command.
### Editor
The Backstage development environment does not require any specific editor, but it is intended to be used with one that has built-in linting and type-checking. The development server does not include any checks by default, but they can be enabled using the `--check` flag. Note that using the flag may consume more system resources and slow things down.
## Package Scripts
There are many commands to be found in the root [package.json](https://github.com/backstage/backstage/blob/master/package.json), here are some useful ones:
```shell
yarn start # Start serving the example app, use --check to include type checks and linting
yarn storybook # Start local storybook, useful for working on components in @backstage/core-components
yarn workspace @backstage/plugin-api-docs start # Serve api-docs plugin only, also supports --check
yarn tsc # Run typecheck, use --watch for watch mode
yarn tsc:full # Run full type checking, for example without skipLibCheck, use in CI
yarn build:backend # Build the backend package, depends on tsc
yarn build:all # Build published versions of packages, depends on tsc
yarn build:api-reports # Build API Reports used for documentation
yarn lint # lint packages that have changed since later commit on origin/master
yarn lint:all # lint all packages
yarn lind:docs # lint all the Markdown files
yarn lint:type-deps # verify that @types/* dependencies are placed correctly in packages
yarn test # test packages that have changed since later commit on origin/master
yarn test:all # test all packages
yarn clean # Remove all output folders and @backstage/cli cache
yarn diff # Make sure all plugins are up to date with the latest plugin template
yarn new # Create a new module
```
> See [package.json](https://github.com/backstage/backstage/blob/master/package.json) for other yarn commands/options.
## Local configuration
Backstage allows you to specify the configuration used while running the application on your computer. Local configuration is read from `app-config.local.yaml`. This file is ignored by Git, which means that you can safely use it to reference secrets like GitHub tokens without worrying about these secrets, inadvertently ending up in the Git repository. You do not need to copy everything from the default config to the local config. The `app-config.local.yaml` file will be merged with `app-config.yaml` and overwrite the default app configs.
> NOTE: If you want to add your own configuration values to access in the frontend you also need to mark those values as visible using configuration schema, either in the app or in your own plugin. For more information, see [Defining Configuration](https://backstage.io/docs/conf/defining/).
You can learn more about the local configuration in the [Static Configuration in Backstage](https://backstage.io/docs/conf/) section.
## Creating Changesets
We use [changesets](https://github.com/atlassian/changesets) to help us prepare releases. They help us make sure that every package affected by a change gets a proper version number and an entry in its `CHANGELOG.md`. To make the process of generating releases easy, it helps when contributors include changesets with their pull requests.
### When to use a changeset?
Any time a patch, minor, or major change aligning to [Semantic Versioning](https://semver.org) is made to any published package in `packages/` or `plugins/`, a changeset should be used. It helps to align your change to the [Backstage package versioning policy](https://backstage.io/docs/overview/versioning-policy#package-versioning-policy) for the package you are changing, for example, when to provide additional clarity on deprecation or impacting changes which will then be included into CHANGELOGs.
In general, changesets are only needed for changes to packages within `packages/` or `plugins/` directories, and only for the packages that are not marked as `private`. Changesets are also not needed for changes that do not affect the published version of each package, for example changes to tests or in-line source code comments.
Changesets **are** needed for new packages, as that is what triggers the package to be part of the next release. They are also needed for changes to `README.md` files so that the updates are reflected on the NPM page for the changed package.
### How to create a changeset
1. Run `yarn changeset` from the root of the repo
2. Select which packages you want to include a changeset for
3. Select impact of the change you're introducing. If the package you are changing is at version `0.x`, use `minor` for breaking changes and `patch` otherwise. If the package is at `1.0.0` or higher, use `major` for breaking changes, `minor` for backwards compatible API changes, and `patch` otherwise. See the [Semantic Versioning specification](https://semver.org/#semantic-versioning-specification-semver) for more details.
4. Explain your changes in the generated changeset. See [examples of well written changesets](#writing-changesets).
5. Add generated changeset to Git
6. Push the commit with your changeset to the branch associated with your PR
7. Accept our gratitude for making the release process easier on the maintainers
### Writing changesets
Changesets are an important part of the development process. They are used to generate Changelog entries for all changes to the project. Ultimately they are read by the end users to learn about important changes and fixes to the project. Some of these fixes might require manual intervention from users so it's important to write changesets that users understand and can take action on.
Here are some important do's and don'ts when writing changesets:
### Changeset should give a clear description to what has changed
#### Bad
```md
---
'@backstage/catalog': patch
---
Fixed table layout
```
#### Good
```md
---
'@backstage/catalog': patch
---
Fixed bug in EntityTable component where table layout did not readjust properly below 1080x768 pixels.
```
### Breaking changes not caught by the type checker should be clearly marked with bold **BREAKING** text
#### Bad
```md
---
'@backstage/catalog': minor
---
getEntity is now a function that returns a Promise.
```
#### Good
```md
---
'@backstage/catalog': minor
---
**BREAKING** The getEntity function now returns a Promise and **must** be awaited from now on.
```
### Changes to code should include a diff of the files that need updating
#### Bad
```md
---
'@backstage/catalog': patch
---
**BREAKING** The catalogEngine now requires a flux capacitor to be passed.
```
#### Good
````md
---
'@backstage/catalog': patch
---
**BREAKING** The catalog createRouter now requires that a `FluxCapacitor` is
passed to the router.
These changes are **required** to `packages/backend/src/plugins/catalog.ts`
```diff
+ import { FluxCapacitor } from '@backstage/time';
+ const fluxCapacitor = new FluxCapacitor();
return await createRouter({
entitiesCatalog,
locationAnalyzer,
locationService,
+ fluxCapacitor,
logger: env.logger,
config: env.config,
});
```
````
## Developer Certificate of Origin
As with other CNCF projects, Backstage has adopted a [Developers Certificate of Origin (DCO)](https://developercertificate.org/). A DCO is a lightweight way for a developer to certify that they wrote or otherwise have the right to submit code or documentation to a project.
@@ -62,44 +291,71 @@ Signed-off-by: Jane Smith <jane.smith@example.com>
In case you forgot to add the line to your most recent commit, you can amend the commit message from the History tab before pushing your branch (GitHub Desktop 2.9 or later).
## Creating Changesets
## API Reports
We use [changesets](https://github.com/atlassian/changesets) to help us prepare releases. They help us make sure that every package affected by a change gets a proper version number and an entry in its `CHANGELOG.md`. To make the process of generating releases easy, it helps when contributors include changesets with their pull requests.
Backstage uses [API Extractor](https://api-extractor.com/) and TSDoc comments to generate API Reports in Markdown format. These reports are what drive the [API Reference documentation](https://backstage.io/docs/reference/). What this means is that if you are making changes to the API or adding a new plugin then you will need either generate a new API Report or update an existing API Report. If you don't do this the CI build will fail when you create your Pull Request.
### When to use a changeset?
There are two ways you can do this:
Any time a patch, minor, or major change aligning to [Semantic Versioning](https://semver.org) is made to any published package in `packages/` or `plugins/`, a changeset should be used. It helps to align your change to the [Backstage package versioning policy](https://backstage.io/docs/overview/versioning-policy#package-versioning-policy) for the package you are changing, for example, when to provide additional clarity on deprecation or impacting changes which will then be included into CHANGELOGs.
1. You can run `yarn build:api-reports` from the root of the project and it will go through all of the existing API Reports and update them or create new ones as needed. This may take a while but is generally the best method if you are new to this.
2. You can run `yarn build:api-reports plugins/<your-plugin-with-changes>` from the root and it will update the existing API Report or create a new one.
In general, changesets are only needed for changes to packages within `packages/` or `plugins/` directories, and only for the packages that are not marked as `private`. Changesets are also not needed for changes that do not affect the published version of each package, for example changes to tests or in-line source code comments.
> Note: the above commands assume you've run `yarn install` before hand or recently
Changesets **are** needed for new packages, as that is what triggers the package to be part of the next release.
Each plugin/package has its own API Report which means you might see more then one file updated or created depending on your changes. These changes will then need to be committed as well.
### How to create a changeset
## Submitting a Pull Request
1. Run `yarn changeset`
2. Select which packages you want to include a changeset for
3. Select impact of the change you're introducing. If the package you are changing is at version `0.x`, use `minor` for breaking changes and `patch` otherwise. If the package is at `1.0.0` or higher, use `major` for breaking changes, `minor` for backwards compatible API changes, and `patch` otherwise. See the [Semantic Versioning specification](https://semver.org/#semantic-versioning-specification-semver) for more details.
4. Explain your changes in the generated changeset. See [examples of well written changesets](https://backstage.io/docs/getting-started/contributors#writing-changesets).
5. Add generated changeset to Git
6. Push the commit with your changeset to the branch associated with your PR
7. Accept our gratitude for making the release process easier on the maintainers
When you've got your contribution working, tested, and committed to your branch it's time to create a Pull Request (PR). If you are unsure how to do this GitHub's [Creating a pull request from a fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) documentation will help you with that. Once you create your PR you will be presented with a template in the PR's description that looks like this:
For more information, check out [adding a changeset](https://github.com/atlassian/changesets/blob/master/docs/adding-a-changeset.md) documentation in the changesets repository.
```md
## Hey, I just made a Pull Request!
## Merging to Master
<!-- Please describe what you added, and add a screenshot if possible.
That makes it easier to understand the change so we can :shipit: faster. -->
#### :heavy_check_mark: Checklist
<!--- Please include the following in your Pull Request when applicable: -->
- [ ] A changeset describing the change and affected packages. ([more info](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#creating-changesets))
- [ ] Added or updated documentation
- [ ] Tests for new functionality and regression tests for bug fixes
- [ ] Screenshots attached (for UI changes)
- [ ] All your commits have a `Signed-off-by` line in the message. ([more info](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#developer-certificate-of-origin))
```
From here all you need to do is fill in the information as requested by the template. Please do not remove this as it helps both you and the reviewers confirm that the various tasks have been completed.
Here are some examples of good PR descriptions:
- <https://github.com/backstage/backstage/pull/19473>
- <https://github.com/backstage/backstage/pull/19623>
- <https://github.com/backstage/backstage/pull/15881>
- <https://github.com/backstage/backstage/pull/16401>
## Review Process
Once you've submitted a Pull Request (PR) the various bots will come out and do their work:
- assigning reviewers from the various areas impacted by changes in your PR
- adding labels to help make reviewing PRs easier
- checking for missing changesets or confirming them
- checking for commits for their DCO (Developer Certificate of Origin)
- kick of the various CI builds
Once these have been completed it's just a matter of being patient as the reviewers have time they will begin to review your PR. When the review begins there may be a few layers to this but the general rule is that you need approval from one of the core maintainers and one from the specific area impacted by your PR. You may also have someone from the community review your changes, this can really help speed things up as they may catch some early items making the review for the maintainers simpler. Once you have the two (2) approvals it's ready to be merged, this task is also done by the maintainers.
### Review Tips
Here are a few things that can help as you go through the review process:
- You'll want to make sure all the automated checks are passing as generally the PR won't get a review if something like the CI build is failing
- PRs get automatically assigned so you don't need to ping people, they will be notified and have a process of their own for this
- If you are waiting for a review or mid-review and your PR goes stale one of the easiest ways to clear the stale bot is by simply rebasing your PR
- There are times where you might run into conflict with the `yarn.lock` during a rebase, to help with that make sure your `master` branch is up to date and then in your branch run `git checkout master yarn.lock` and then run `yarn install`, this will get you a conflict free `yarn.lock` file you can commit
- If Vale finds issues with your documentation but it's a code reference you can fix it by putting backticks (`) around it. Now if it is a special word or maybe a name there are two ways you can fix that by adding it to the list of accepted words in the [accept.txt file](https://github.com/backstage/backstage/blob/master/.github/vale/Vocab/Backstage/accept.txt) and them committing that change
### Merging to Master
For those contributors who have earned write access to the repository, when a pull request is approved, in general we prefer the author of the PR to perform the merge themselves. This allows them to own accountability for the change and they likely know best how or when to address pending fixes or additional follow-ups. In this way, we all help contribute to the project's successful outcomes.
## Code of Conduct
This project adheres to the [CNCF Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code.
[code-of-conduct]: https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md
## Security Issues?
See [SECURITY](SECURITY.md).
## Accessibility
We encourage you to catch any accessibility issues already in the development phase of new features to Backstage, see our [Accessibility documentation](./docs/accessibility/index.md) for more details.
@@ -1,4 +1,4 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="642px" height="327px" viewBox="-0.5 -0.5 642 327" content="&lt;mxfile scale=&quot;1.5&quot; border=&quot;20&quot;&gt;&lt;diagram id=&quot;gKBvn7eBFHudv9VMIRD-&quot; name=&quot;Page-1&quot;&gt;3VnbTuMwEP2aPFLl1tsjlMLuw0pI3dXCo0ncxMK1K8dtU75+7dhJnBhKFMJlGySUGV/nnDNjE5xgsclvGdimv2gMseO7ce4E147vz1xX/JaOo3JMZ9qRMBQrl1c7VugZamfZbYdimDU6ckoxR9umM6KEwIg3fIAxemh2W1PcXHULEmg5VhHAtvcvinmqvEHo1v4fECVpuXIZ3iOInhJGd0Qv5/jBunhU8waUU+n+WQpiejBcwdIJFoxSrt42+QJiiWyJmhp380prtW0GCe8ywFcD9gDvdORXIgJI4kzvjx9LSIqooBznOsHVIUUcrrYgkq0HoQHhS/kGC8sTr2uE8YJiyoqxwXIif4Q/44w+QaNlUjxyBCXc8KtH+O2IdJB7yDjMDZeO8BbSDeTsKLro1otAo63FeFGif6ipDUvFpgatJd1Aqymppq4hFS8a1ZcRDiyEV5DtUQTPCWGvhfDUBnj2QfjOLXzvGN2L6mHBC2OR4toklMAmnjBH/N54f5A0jMbaus41K4VxLA0SX8piI8xHTKOn3ykiyn2DcDUxKetbWNADGDfsJueSIt04P8VMRncs0mGFujwClkDda6xcMuCT7DGIAUf7Zs17iQw99I4isZG6C12vM7Fmm61qhU4EenYN+pP1Zc8dTccmgW/QJzZ5bxoG6dKshxXWh9EuEw2Jw+cSo0TMdL1BcYyLCClDz0IToMr6YQQSdBSITm935Lr+TI35FpoJ7aTHuwSRc6qp7VOrqrGfUFTHFr7LnEOSIUqEu6D0nKBuH1+fCXV54zWwXgDZo1/9+99Or8knnF6dqbDvau85ifweJ5H3xSfRQBR2PV8Gp3BiMSj+NN3hs7ps+/Ovq1bVbmuAfxIhqf4Fa+TO50aeeCM37Hhpq7PjwWz79pc2Gc4dZEiAD5merFOi+XaihUMnWudbW90nb8qrVGVbbioSPejEPF57Irc1kYrfmqjHNfKFzx891XwWolQBtLLRP52NA2p58n20HLQuhH217L+REwNK2b679PwOckZSNi9g3kkdxyBLi5W9dwh48FvPOwTcKqKBO5rOjWc87ifo9rzDKVqY9Wdv1b3+z0Kw/Ac=&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);">
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="642px" height="327px" viewBox="-0.5 -0.5 642 327" content="&lt;mxfile scale=&quot;1.5&quot; border=&quot;20&quot;&gt;&lt;diagram id=&quot;gKBvn7eBFHudv9VMIRD-&quot; name=&quot;Page-1&quot;&gt;3Vldb9owFP01PBblCyiPHaXdHiZVYtPaRzcxiVVjI8dA6K+fHTuJEzOahZQiUqmKj79y7zn3+iYM/Nkqe2RgnfykEcQDz4mygX8/8Lxg6on/EtgrYORNFBAzFCnIrYAFeocadDS6QRFMawM5pZijdR0MKSEw5DUMMEZ39WFLiuu7rkEMLWARAmyjf1DEE4X6gVPh3yGKk2LnW93xCsK3mNEN0dsNPH+ZX6p7BYql9Pg0ARHdGZA/H/gzRilXd6tsBrH0bOE1Ne/hH73lYzNIeJsJmqYtwBtt+TdhASRRqp+P7wuX5FZBOc8Z+N92CeJwsQah7N0JDQgs4SssWq64XSKMZxRTls/152P5J/CUM/oGjZ5xfskZlHADV5fAbYu0kVvIOMwMSFv4COkKcrYXQ3Tvja+9rcV4U3h/V1EbOBpLDFoLuoFWU1wuXblU3GivHvawb3l4AdkWhfCaPOw2PDyxHXz7Sf6dWv59YnQrsoflXhiJENdNQgms+xNmiD8b9y+ShuFIt+4zzUre2BcNEt3JZCOar5iGb78SRBT8gHC5MCnyW5DTAxg32nXOJUW6c3qMmZRuWKjNCnR6BCyGetRIQdLgo+wxiAFH23rOO0SGnvpEkXiQaghdLlOxZ5OtcodWBLp2DvqddmXPGU5GJoEf0Cce8tlsGKTLZjUtb30a7TLQkDh87jCKxUr3KxRFOLeQMvQuNAHKqO9HIH5LgejwdoaO492qORehmcAOeryJEbmmnNo8tcoce4akOrL8O884JCmiRMA5pdfk6ubxdU5XFxVvbweYY+a/Kh1+zQlWpli3nmInH+XYVpltfIajrzWPdqF3yjHmdTjGGj4++zHW5XA6QGHbw6l3CscWg+K9doOvqlL3pl+X6sqnrRz8gwhJyUEdw8SZTo04cYdO0LLiq6Ljxey7+IpPmvMEGRLOh+x/cqVnB1rQd6C1LvmqMVldXoUqm3JTluhJR9Zxmws5jYWU/dZCHWrQA99OOqr5KkSpDGhEo3c8GnvU8vhytOw3qsmuWvY+iIkepWzXLh1r0CuSslmAuUd1HIE0yXd2TxBw71XPCQJuJFFf1OtT4xqNugm6ue5nKtr+QjA7oc6ovVJ5l/lK5bV7pWoIrPM7Vn/KLNThNNQxkZ+dqsvtJjr19cpapt0mnQUpmtWPOGp49TuZP/8L&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);">
<defs/>
<g>
<rect x="19.5" y="19.5" width="600" height="60" fill="#e6e6e6" stroke="#666666" stroke-width="1.5" pointer-events="all"/>
@@ -105,21 +105,21 @@
</text>
</switch>
</g>
<path d="M 499.5 274.5 L 388.68 274.5" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 381.18 274.5 L 388.68 272 L 388.68 277 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 499.5 289.5 L 388.68 289.5" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 381.18 289.5 L 388.68 287 L 388.68 292 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)scale(1.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 183px; margin-left: 293px;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 193px; margin-left: 293px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Call
Provide
</div>
</div>
</div>
</foreignObject>
<text x="293" y="186" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="9px" text-anchor="middle">
Call
<text x="293" y="196" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="9px" text-anchor="middle">
Provide
</text>
</switch>
</g>
@@ -171,7 +171,7 @@
</div>
</div>
</foreignObject>
<text x="53" y="112" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="9px" text-anchor="middle">
<text x="53" y="111" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="9px" text-anchor="middle">
Install
</text>
</switch>
@@ -181,7 +181,7 @@
<g transform="translate(-0.5 -0.5)scale(1.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 108px; margin-left: 373px;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 109px; margin-left: 373px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Install
@@ -212,6 +212,24 @@
</text>
</switch>
</g>
<path d="M 499.5 259.5 L 388.68 259.5" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 381.18 259.5 L 388.68 257 L 388.68 262 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)scale(1.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 173px; margin-left: 293px;">
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
Call
</div>
</div>
</div>
</foreignObject>
<text x="293" y="176" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="9px" text-anchor="middle">
Call
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB

+7 -7
View File
@@ -10,6 +10,13 @@ configure Backstage to have any number of authentication providers, but only
one of these will typically be used for sign-in, with the rest being used to provide
access to external resources.
> NOTE: Identity management and the Sign-In page in Backstage is NOT a method for blocking
> access for unauthorized users. The identity system only serves to provide a personalized
> experience and access to a Backstage Identity Token, which can be passed to backend plugins.
> This also means that your Backstage backend APIs are by default unauthenticated.
> Thus, if your Backstage instance is exposed to the Internet, anyone can access
> information in the Backstage. You can learn more [here](../overview/threat-model.md#integrator-responsibilities).
## Built-in Authentication Providers
Backstage comes with many common authentication providers in the core library:
@@ -60,13 +67,6 @@ the local `auth.environment` setting will be selected.
## Sign-In Configuration
> NOTE: Identity management and the `SignInPage` in Backstage is NOT a method
> for blocking access for unauthorized users, that either requires additional
> backend implementation or a separate service like Google's Identity-Aware
> Proxy. The identity system only serves to provide a personalized experience
> and access to a Backstage Identity Token, which can be passed to backend
> plugins.
Using an authentication provider for sign-in is something you need to configure
both in the frontend app, as well as the `auth` backend plugin. For information
on how to configure the backend app, see [Sign-in Identities and Resolvers](./identity-resolver.md).
+4 -4
View File
@@ -43,15 +43,15 @@ Services are also a customization point for individual backend installations. Yo
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 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 or modules themselves, based on what customizations each of them want 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 deprecate individual Extension Points over time, rather than dealing with a single large API surface.
Extension Points are exported separately from the plugin or module instance itself, and it is possible to expose multiple different extension points at once. This makes it easier to evolve and deprecate 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.
Modules use Extension Points to add new features to other plugins or modules. They might for example add an individual Catalog Entity Provider, or one or more Scaffolder Actions.
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.
Each module may only use Extension Points that belong to a single plugin, and the module must be deployed together with that plugin in the same backend instance. Modules may only communicate with their plugin or other modules through the 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.
@@ -17,24 +17,27 @@ Below is a simple example of a backend that installs only the catalog plugin and
```ts
import { createBackend } from '@backstage/backend-defaults';
import { catalogPlugin } from '@backstage/plugin-catalog-backend';
import scaffolderPlugin from '@backstage/plugin-scaffolder-backend';
// Create your backend instance
const backend = createBackend();
// Install all desired features
backend.add(catalogPlugin());
// Install desired features
backend.add(import('@backstage/plugin-catalog-backend'));
// Features can also be installed using an explicit reference
backend.add(scaffolderPlugin());
// Start up the backend
await backend.start();
backend.start();
```
`createBackend` is responsible for creating your backend instance, and wiring up all the services that you have provided. It deals with creating default implementations of all the [core services](../core-services/01-index.md) that are used by the plugins, and also provides a way to override the default implementations with your own. You can read more about creating services and overriding them in the [building backends docs](../building-backends/01-index.md).
We call `createBackend` to create a new backend instance, which is responsible for wiring together all of the features that we provide to the app. It also provides default implementations of all [core services](../core-services/01-index.md) for use in plugins. No real work is done at the point of creating the backend though, it's all deferred to the `backend.start()` call.
The backend instance has the ability to add features to the backend which are done using the `.add` method. Features are either plugins or modules, and you can read more about them in the [building plugins and modules docs](../building-plugins-and-modules/01-index.md). By default, a backend instance has no default features, and the services are responsible for wiring everything together.
To add any feature to a backend instance you use the `.add(...)` method. Features are either plugins, modules, or service factories. You can read more about building plugins and modules in the [building plugins and modules docs](../building-plugins-and-modules/01-index.md), as well as how to install services factories in the [building backends docs](../building-backends/01-index.md).
At a high level, when you call `createBackend`, it will create a new backend instance, which has a registry of all the services that are currently registered, and by adding features to the backend instance and calling the `.start()` method it will ensure that all the dependencies are wired up correctly and the `registerInit` methods are called in the correct order.
Once you have added all desired features we call the `.start()` method. This causes the backend to start up and initialize all features. When starting up the backend will validate all features to make sure that there are no conflicts. For example making sure that there are no circular dependencies.
Underneath the hood, `createBackend` calls `createSpecializedBackend` from `@backstage/backend-app-api` which is responsible for actually creating the backend instance, but with no services or no features. You can think of `createBackend` more of a 'batteries included' approach, and `createSpecializedBackend` a little more low level.
Underneath the hood, `createBackend` calls `createSpecializedBackend` from `@backstage/backend-app-api` which is responsible for actually creating the backend instance, without any services or features. You can think of `createBackend` more of a 'batteries included' approach, while `createSpecializedBackend` is more low level.
As mentioned previously there's also the ability to create multiple of these backends in your project so that you can split apart your backend and deploy different backends that can scale independently of each other. For instance you might choose to deploy a backend with only the catalog plugin enabled, and one with just the scaffolder plugin enabled.
+37 -36
View File
@@ -88,42 +88,6 @@ export const fooServiceFactory = createServiceFactory({
Note that circular dependencies among service factories are not allowed. This is verified at runtime, and your backend instance will refuse to start up if it detects any conflicts. Likewise, the backend will also fail to start up if a service factory depends on a service that is not provided by any registered service factory.
## Service Factory Options
To install a service factory in a backend instance, we pass it in through the `services` option to `createBackend`:
```ts
const backend = createBackend({
services: [fooServiceFactory()],
});
```
Note that we call `fooServiceFactory` to create the service factory instance. This is because `createServiceFactory` always returns a factory function that creates the actual service factory. This is done to always allow for options to be added to the service factory in the future, without breaking existing code. To add options to your service factory, you wrap the object passed to `createServiceFactory` in a callback that accepts the desired options. For example:
```ts
export interface FooFactoryOptions {
mode: 'eager' | 'lazy';
}
export const fooServiceFactory = createServiceFactory(
(options?: FooFactoryOptions) => ({
service: fooServiceRef,
deps: { bar: barServiceRef },
factory({ bar }) {
return new DefaultFooService(bar, options?.mode);
},
}),
);
```
This lets us use the options to customize the factory implementation in any way we want. From the outside the service factory looks just like before, except that we're now also able to pass options when installing the factory:
```ts
const backend = createBackend({
services: [fooServiceFactory({ mode: 'eager' })],
});
```
## Core Services
The backend system provides a number of core service definitions that both help implement the main functionality of the backend, but also provide a set of utilities for common concerns, such as logging, database access, job scheduling, and so on. These core services will always be present in a backend instance created with `createBackend`, and they can all be overridden with custom implementations if needed.
@@ -227,3 +191,40 @@ Note that we don't use the `fooServiceRef` when creating our service factory, bu
If a service defines a default factory, that factory will be used if there is no explicit factory registered in the backend for that service. This allows users of your service to directly import and use a service, without worrying about whether it is installed or not. It is recommended to always define a default factory for any service that you are exporting for use in other plugins or modules.
When defining a default factory for a service, it is possible for it to end up with duplicate implementations at runtime. This applies both to any shared root context in your factory, as well as plugin specific instances of your service. This is because package dependency version ranges may not line up perfectly, causing duplicate installations of the same package. This can happen both for two different plugins using the same service, but also across a plugin and its modules. If your service would break in this scenario, you should not define a default factory for it, but instead require that users of your service explicitly install a factory in their backend instance.
## Service Factory Options
> NOTE: This pattern is discouraged, only use it when necessary. If possible you should prefer to make services configurable via static configuration instead.
When declaring a service factory it's possible to include an options callback. This allows you to customize the factory through code when installing it in the backend. For example, this is how you install an explicit factory instance in the backend without any options:
```ts
const backend = createBackend();
backend.add(fooServiceFactory());
```
Note that we call `fooServiceFactory` to create the service factory instance. This is because `createServiceFactory` always returns a factory function that creates the actual service factory. To add options to your service factory, you wrap the object passed to `createServiceFactory` in a callback that accepts the desired options. Note that the options must always be optional. For example:
```ts
export interface FooFactoryOptions {
transform: (foo: string) => string;
}
export const fooServiceFactory = createServiceFactory(
(options?: FooFactoryOptions) => ({
service: fooServiceRef,
factory() {
return new DefaultFooService(options?.transform);
},
}),
);
```
This lets us use the options to customize the factory implementation in any way we want. From the outside the service factory looks just like before, except that we're now also able to pass options when installing the factory:
```ts
const backend = createBackend();
backend.add(fooServiceFactory({ transform: foo => foo.toUpperCase() }));
```
+14 -26
View File
@@ -41,44 +41,32 @@ The `env` object passed to the `register` callback contains different methods th
The `createBackendPlugin` return value is exported as `examplePlugin`, which is a factory function used to create the actual plugin instance. For example, to install the plugin in your backend instance, you would do the following:
```ts
import { examplePlugin } from 'backstage-plugin-example-backend';
backend.add(examplePlugin());
```
The reason for why our plugin instance has been wrapped up in a factory function is so that you can always chose to add options to your plugin in the future, without breaking existing usage. To add options you wrap the object passed to `createBackendPlugin` in a callback that accepts the desired options. For example:
By convention every plugin package should export its plugin instance as the default export from the package:
```ts
export interface ExamplePluginOptions {
skipHello: boolean;
}
export const examplePlugin = createBackendPlugin(
(options?: ExamplePluginOptions) => ({
pluginId: 'example',
register(env) {
env.registerInit({
deps: {
logger: coreServices.logger,
},
async init({ logger }) {
if (!options?.skipHello) {
logger.info('Hello from example plugin');
}
},
});
},
}),
);
// plugins/example-backend/src/index.ts
export { examplePlugin as default } from './plugin.ts';
```
Now your plugin accepts an optional options object, which can be used to configure each plugin instance. To supply options to the plugin, you pass them to the plugin factory method:
This allows you to install the plugin in your backend instance by just referencing the package:
```ts
backend.add(examplePlugin({ skipHello: true }));
backend.add(import('backstage-plugin-example-backend'));
```
It is also possible to make the options required, simply remove the `?` from the parameter declaration. This will be reflected in the returned factory function, which will now require the options parameter.
To make your plugins customizable you should generally prefer to use [static configuration](../../conf/defining.md). By convention plugins should place their configuration under a top-level configuration key that matches the plugin ID. For example, our example plugin might be configured as follows:
Options are a simple way to allow for more lightweight customization of a plugin, but they do not allow for more complex extensions that require access to services. For that, you need to create and register extension points for your plugin, which are covered in the [next section](./05-extension-points.md).
```yaml
example:
message: Welcome to the example plugin
```
For situations where static configuration is too limiting, you can instead register extension points for your plugin. Extension Points are covered in the [next section](./05-extension-points.md).
## Rules of Plugins
@@ -8,11 +8,11 @@ description: Extension points of backend plugins
> **DISCLAIMER: The new backend system is in alpha, and still under active development. While we have reviewed the interfaces carefully, they may still be iterated on before the stable release.**
While plugins are able to accept options for lightweight forms of customization and extension, you quickly hit a limit where you need something more powerful to allow users to extend your plugin. For this purpose, the backend system provides a mechanism for plugins to provide extension points, which can be used to expose deeper customizations for your plugin. Extension points are used by modules, which are installed in the backend adjacent to plugins. Modules are covered more in-depth in the [next section](./06-modules.md).
While plugins are able to use static configuration for lightweight forms of customization, you can quickly hit a limit where you need something more powerful to allow users to extend your plugin. For this purpose, the backend system provides a mechanism for plugins to provide extension points, which can be used to expose deeper customizations for your plugin. Extension points are used by modules, which are installed in the backend adjacent to plugins. Modules are covered more in-depth in the [next section](./06-modules.md).
Extension points are quite similar to services, in that they both encapsulate an interface in a reference object. The key difference is that extension points are registered and provided by plugins themselves, and do not have any factory associated with them. Extension points for a given plugin are also only accessible to modules that extend that same plugin.
Extension points should always be exported from a plugin node library package, for example `@backstage/plugin-catalog-node`. This is to allow for modules to avoid a direct dependency on the plugin, and make it easier to evolve extension points over time. You can export as many different extension points as you want, just be mindful of the complexity of the API surface. It is however often better to export multiple extension points with few methods, rather than few extension points with many methods, as that tends to be easier to maintain.
Plugin extension points should always be exported from a plugin node library package, for example `@backstage/plugin-catalog-node`. This is to allow for modules to avoid a direct dependency on the plugin, and make it easier to evolve extension points over time. You can export as many different extension points as you want, just be mindful of the complexity of the API surface. It is however often better to export multiple extension points with few methods, rather than few extension points with many methods, as that tends to be easier to maintain.
## Defining an Extension Point
@@ -36,28 +36,29 @@ export const scaffolderActionsExtensionPoint =
For modules to be able to use your extension point, an implementation of it must be registered by the plugin. This is done using the `registerExtensionPoint` method in the `register` callback of the plugin definition.
```ts
class ActionsExtension implements ScaffolderActionsExtensionPoint {
addActions(...actions: TemplateAction<any>[]): void { ... }
getRegisteredActions() { ... }
}
export const scaffolderPlugin = createBackendPlugin(
{
pluginId: 'scaffolder',
register(env) {
const actionsExtensions = new ActionsExtension();
const actions = new Map<string, TemplateAction<any>>();
env.registerExtensionPoint(
scaffolderActionsExtensionPoint,
actionsExtensions,
{
addAction(action) {
if (actions.has(action.id)) {
throw new Error(`Scaffolder actions with ID '${action.id}' has already been installed`);
}
actions.set(action.id, action);
},
},
);
env.registerInit({
deps: { ... },
async init({ ... }) {
const actions = actionsExtension.getRegisteredActions();
// Use the registered actions when setting up the scaffolder ...
const installedActions = Array.from(actions.values());
},
});
},
@@ -65,9 +66,11 @@ export const scaffolderPlugin = createBackendPlugin(
);
```
There are a couple of things to note here. The first is that our `ActionsExtension` class both implements the `ScaffolderActionsExtensionPoint` interface, but also has additional public methods. These methods won't be available to the modules that use this extension, but we can use them here in the implementation of our plugin. Note also that the `ActionsExtension` class is _not_ exported to the outside, since it's only for the internal use of this plugin during its setup phase.
Note that we create a closure that adds to a shared `actions` structure when `addAction` is called by users of your extension point. It is safe for us to then access our `actions` in the `init` method of our plugin, since all modules that extend our plugin will be completely initialized before our plugin gets initialized. That means that at the point where our `init` method is called, all actions have been added and can be accessed.
The second is that we create our `ActionsExtension` instance within the `register` method, and then access it directly in our `init` method. This is both safe to do and an intended convenience. All modules that extend our plugin will be completely initialized before our plugin gets initialized, which means that at the point where our `init` method is called, all actions have been added and can be accessed.
## Module Extension Points
Just like plugins, modules can also provide their own extension points. The API for registering and using extension points is the same as for plugins. However, modules should typically only use extension points to allow for complex internal customizations by users of the plugin module. It is therefore preferred to export the extension point directly from the module package, rather than creating a separate node library for that purpose.
## Extension Point Design
+18 -2
View File
@@ -8,7 +8,7 @@ description: Modules for backend plugins
> **DISCLAIMER: The new backend system is in alpha, and still under active development. While we have reviewed the interfaces carefully, they may still be iterated on before the stable release.**
Backend modules are used to extend [plugins](./04-plugins.md) with additional features or change existing behavior. They must always be installed in the same backend instance as the plugin that they extend, and may only extend a single plugin. Modules interact with their target plugin using the [extension points](./05-extension-points.md) registered by the plugin, while also being able to depend on the [services](./03-services.md) of that plugin.
Backend modules are used to extend [plugins](./04-plugins.md) or sometimes other modules with additional features or change existing behavior. They must always be installed in the same backend instance as the plugin that they extend, and may only extend a single plugin. Modules interact with their target plugin using the [extension points](./05-extension-points.md) registered by the plugin, while also being able to depend on the [services](./03-services.md) of that plugin.
Both modules and plugins register an `init` method that is called during startup. In order to ensure that modules have registered all their extensions before the plugin starts up, all modules for each plugin are completely initialized before the plugin itself is initialized. In practice this means that all promises returned by each `init` method of the modules need to resolve before the plugin `init` method is called. This also means that it is not possible to further interact with the extension points once the `init` method has resolved.
@@ -19,6 +19,7 @@ A module depends on the extension points exported by the target plugin's library
The following is an example on how to create a module that adds a new processor using the `catalogProcessingExtensionPoint`:
```ts
// plugins/catalog-backend-module-example-processor/src/module.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';
import { MyCustomProcessor } from './MyCustomProcessor';
@@ -42,4 +43,19 @@ export const catalogModuleExampleCustomProcessor = createBackendModule({
Notice that we're placing the extension point we want to interact with in the `deps` option, while also depending on the logger service at the same time. When initializing modules we can depend on both extension points and services interchangeably. You can also depend on multiple extension points at once, in case the implementation of the module requires it.
It is typically best to keep modules slim and to each only add a single new feature. It is often the case that it is better to create two separate modules rather than one that provides both features. The one limitation here is that modules can not interact with each other and need to be self contained.
Just like plugins there is a convention that every module package should export its module instance as the default export from the package:
```ts
// plugins/catalog-backend-module-example-processor/src/index.ts
export { catalogModuleExampleCustomProcessor as default } from './module.ts';
```
This allows you to install the module in your backend instance by just referencing the package:
```ts
backend.add(
import('backstage-plugin-catalog-backend-module-example-processor'),
);
```
Each module package should only contain a single module, but this module may extend multiple extension points. A module may also use configuration to conditionally enable or disable certain extensions. This pattern should only be used for extensions that are related to each other, otherwise it is best to create a separate module package with its own module.
@@ -20,27 +20,23 @@ A minimal Backstage backend is very lightweight. It is a single package with a `
When you create a new project with `@backstage/create-app`, you'll get a backend package with a `src/index.ts` that looks something like this:
```ts
import { createBackend } from '@backstage/backend-defaults';
import { appPlugin } from '@backstage/plugin-app-backend';
import { catalogPlugin } from '@backstage/plugin-catalog-backend';
import {
scaffolderPlugin,
catalogModuleTemplateKind,
} from '@backstage/plugin-scaffolder-backend';
import { createBackend } from '@backstage/backend-defaults'; // Omitted in the examples below
const backend = createBackend();
backend.add(appPlugin());
backend.add(catalogPlugin());
backend.add(catalogModuleTemplateKind());
backend.add(scaffolderPlugin());
backend.add(import('@backstage/plugin-app-backend'));
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.start();
```
There will be a couple more plugins and modules in the initial setup, but the overall layout is the same.
What we're doing in this file is creating a new backend using `createBackend`, and then installing a collection of different plugins and modules that we want to be part of that backend. Plugins are standalone features, while modules augment existing plugins. Each module can only target a single plugin, and that plugin must also be present in the same backend. Finally, we start up the backend by calling the `start` method.
What we're doing in this file is creating a new backend using `createBackend`, and then installing a collection of different plugins, modules, and services that we want to be part of that backend. Plugins are standalone features, modules augment existing plugins or modules, while services can be used to override behavior for deeper customizations. Each module can only target a single plugin, and that plugin must also be present in the same backend. Finally, we start up the backend by calling the `start` method.
## Customization
@@ -63,13 +59,13 @@ For example, let's say we want to customize the core configuration service to en
```ts
import { rootConfigServiceFactory } from '@backstage/backend-app-api';
const backend = createBackend({
services: [
rootConfigServiceFactory({
remote: { reloadIntervalSeconds: 60 },
}),
],
});
const backend = createBackend();
backend.add(
rootConfigServiceFactory({
remote: { reloadIntervalSeconds: 60 },
}),
);
```
This will make it possible to pass URLs as configuration targets, and those URLs will be polled every 60 seconds for changes.
@@ -83,22 +79,22 @@ When overriding services you are not limited to the existing implementations, yo
To override a service, you provide it in the `services` option just like above, but this time we need to use `createServiceFactory` to create our factory. For example, if you want to replace the default `LoggerService` with your own, it might look like this:
```ts
const backend = createBackend({
services: [
createServiceFactory({
service: coreServices.logger,
deps: {
rootLogger: coreServices.rootLogger,
plugin: coreServices.pluginMetadata,
config: coreServices.config,
},
factory({ rootLogger, plugin, config }) {
const labels = readCustomLogLabelsForPlugin(config, plugin); // custom logic
return rootLogger.child(labels);
},
}),
],
});
const backend = createBackend();
backend.add(
createServiceFactory({
service: coreServices.logger,
deps: {
rootLogger: coreServices.rootLogger,
plugin: coreServices.pluginMetadata,
config: coreServices.rootConfig,
},
factory({ rootLogger, plugin, config }) {
const labels = readCustomLogLabelsForPlugin(config, plugin); // custom logic
return rootLogger.child(labels);
},
}),
);
```
The `LoggerService` is responsible for creating a specialized logger instance for each plugin, while the `RootLoggerService` is the actual logging implementation. The default implementation of `LoggerService` will decorate the logger with a `plugin` label that contains the plugin ID. Here in our custom implementation we read out additional labels from the configuration and add those as well.
@@ -128,22 +124,22 @@ packages/
You can now trim down the `src/index.ts` files to only include the plugins and modules that you want to be part of that backend. For example, if you want to split out the scaffolder plugin, you might end up with something like this:
```ts
// packages/backend-a/src/index.ts, imports omitted
const backend = createBackend();
backend.add(appPlugin());
backend.add(catalogPlugin());
backend.add(catalogModuleTemplateKind());
backend.add(import('@backstage/plugin-app-backend'));
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.start();
```
And `backend-b`, don't forget to clean up dependencies in `package.json` as well:
```ts
// packages/backend-b/src/index.ts, imports omitted
const backend = createBackend();
backend.add(scaffolderPlugin());
backend.add(import('@backstage/plugin-scaffolder-backend'));
backend.start();
```
@@ -145,7 +145,7 @@ import { coreServices } from '@backstage/backend-plugin-api';
const legacyPlugin = makeLegacyPlugin(
{
cache: coreServices.cache,
config: coreServices.config,
config: coreServices.rootConfig,
database: coreServices.database,
discovery: coreServices.discovery,
logger: coreServices.logger,
@@ -232,18 +232,13 @@ The app backend plugin that serves the frontend from the backend can trivially
be used in its new form.
```ts title="packages/backend/src/index.ts"
/* highlight-add-next-line */
import { appPlugin } from '@backstage/plugin-app-backend';
const backend = createBackend();
/* highlight-add-next-line */
backend.add(appPlugin({ appPackageName: 'app' }));
backend.add(import('@backstage/plugin-app-backend'));
```
This is an example of how options can be passed into some backend plugins. The
app plugin specifically needs to know the name of the package that holds the
frontend code. This is the `"name"` field in that package's `package.json`,
typically found in your `packages/app` folder. By default it's just plain "app".
If you need to override the app package name, which otherwise defaults to `"app"`,
you can do so via the `app.packageName` configuration key.
You should be able to delete the `plugins/app.ts` file at this point.
@@ -252,21 +247,18 @@ You should be able to delete the `plugins/app.ts` file at this point.
A basic installation of the catalog plugin looks as follows.
```ts title="packages/backend/src/index.ts"
/* highlight-add-start */
import { catalogPlugin } from '@backstage/plugin-catalog-backend';
import { catalogModuleTemplateKind } from '@backstage/plugin-scaffolder-backend';
/* highlight-add-end */
const backend = createBackend();
/* highlight-add-start */
backend.add(catalogPlugin());
backend.add(catalogModuleTemplateKind());
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
/* highlight-add-end */
```
Note that this also installs a module from the scaffolder, namely the one which
enables the use of the `Template` kind. In the unlikely event that you do not
use templates at all, you can remove those lines.
Note that this also installs the scaffolder module for the catalog, which
enables the use of the `Template` kind. In the event that you do not
use templates at all, you can remove that line.
If you have other customizations made to `plugins/catalog.ts`, such as adding
custom processors or entity providers, read on. Otherwise, you should be able to
@@ -305,8 +297,10 @@ const catalogModuleCustomExtensions = createBackendModule({
/* highlight-add-end */
const backend = createBackend();
backend.add(catalogPlugin());
backend.add(catalogModuleTemplateKind());
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
/* highlight-add-next-line */
backend.add(catalogModuleCustomExtensions());
```
@@ -330,12 +324,9 @@ implementations that they represent, and being exported from there.
A basic installation of the events plugin looks as follows.
```ts title="packages/backend/src/index.ts"
/* highlight-add-next-line */
import { eventsPlugin } from '@backstage/plugin-events-backend';
const backend = createBackend();
/* highlight-add-next-line */
backend.add(eventsPlugin());
backend.add(import('@backstage/plugin-events-backend'));
```
If you have other customizations made to `plugins/events.ts`, such as adding
@@ -374,7 +365,7 @@ const eventsModuleCustomExtensions = createBackendModule({
/* highlight-add-end */
const backend = createBackend();
backend.add(eventsPlugin());
backend.add(import('@backstage/plugin-events-backend'));
/* highlight-add-next-line */
backend.add(eventsModuleCustomExtensions());
```
@@ -398,12 +389,9 @@ implementations that they represent, and being exported from there.
A basic installation of the scaffolder plugin looks as follows.
```ts title="packages/backend/src/index.ts"
/* highlight-add-next-line */
import { scaffolderPlugin } from '@backstage/plugin-scaffolder-backend';
const backend = createBackend();
/* highlight-add-next-line */
backend.add(scaffolderPlugin());
backend.add(import('@backstage/plugin-scaffolder-backend'));
```
If you have other customizations made to `plugins/scaffolder.ts`, such as adding
@@ -442,7 +430,7 @@ const scaffolderModuleCustomExtensions = createBackendModule({
/* highlight-add-end */
const backend = createBackend();
backend.add(scaffolderPlugin());
backend.add(import('@backstage/plugin-scaffolder-backend'));
/* highlight-add-next-line */
backend.add(scaffolderModuleCustomExtensions());
```
@@ -27,6 +27,7 @@ To create a Backend plugin, run `yarn new`, select `backend-plugin`, and fill ou
A basic backend plugin might look as follows:
```ts
// src/plugin.ts
import {
createBackendPlugin,
coreServices,
@@ -55,6 +56,9 @@ export const examplePlugin = createBackendPlugin({
});
},
});
// src/index.ts
export { examplePlugin as default } from './plugin';
```
When you depend on `plugin` scoped services, you'll receive an instance of them
@@ -91,6 +95,7 @@ The following is an example of how to create a module that adds a new processor
using the `catalogProcessingExtensionPoint`:
```ts
// src/module.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';
import { MyCustomProcessor } from './MyCustomProcessor';
@@ -110,6 +115,9 @@ export const catalogModuleExampleCustomProcessor = createBackendModule({
});
},
});
// src/index.ts
export { catalogModuleExampleCustomProcessor as default } from './module';
```
See [the article on naming patterns](../architecture/07-naming-patterns.md) for
@@ -122,10 +130,11 @@ initializing modules we can depend on both extension points and services
interchangeably. You can also depend on multiple extension points at once, in
case the implementation of the module requires it.
It is typically best to keep modules slim and to each only add a single new
feature. It is often the case that it is better to create two separate modules
rather than one that provides both features. The one limitation here is that
modules can not interact with each other and need to be self contained.
Each module package should only contain a single module, but this module may
extend multiple extension points. A module may also use configuration to
conditionally enable or disable certain extensions. This pattern should only be
used for extensions that are related to each other, otherwise it is best to
create a separate module package with its own module.
### HTTP Handlers
@@ -187,34 +196,25 @@ export const examplesExtensionPoint =
id: 'example.examples',
});
// This is the implementation of the extension point, which is internal to your plugin.
class ExamplesExtension implements ExamplesExtensionPoint {
#examples: Example[] = [];
addExample(example: Example): void {
this.#examples.push(example);
}
// Note that this method is internal to this implementation
getRegisteredExamples() {
return this.#examples;
}
}
// The following shows how your plugin would register the extension point
// and use the features that other modules have registered.
export const examplePlugin = createBackendPlugin({
pluginId: 'example',
register(env) {
const examplesExtensions = new ExamplesExtension();
env.registerExtensionPoint(examplesExtensionPoint, examplesExtensions);
// We can share data between the extension point implementation and our init method.
const examples = new Array<Example>();
// This registers the implementation of the extension point, which is internal to your plugin.
env.registerExtensionPoint(examplesExtensionPoint, {
addExample(example) {
examples.push(example);
},
});
env.registerInit({
deps: { logger: coreServices.logger },
async init({ logger }) {
// We can access `examplesExtension` directly, giving us access to the internal interface.
const examples = examplesExtension.getRegisteredExamples();
// We can access `examples` directly
logger.info(`The following examples have been registered: ${examples}`);
},
});
@@ -224,12 +224,10 @@ export const examplePlugin = createBackendPlugin({
This is a very common type of extension point, one where modules are given the opportunity to register features to be used by the plugin. In this case modules are able to register examples that are then used by our examples plugin.
Note that the public extension point interface only needs to expose the `addExample` method, while the `getRegisteredExamples()` method is kept internal to the plugin.
### Configuration
Your plugin or module can leverage the app configuration to configure its own
internal behavior. You do this by adding a dependency on `coreServices.config`
internal behavior. You do this by adding a dependency on `coreServices.rootConfig`
and reading from that. This pattern is a good fit especially for customization
that needs to be different across environments.
@@ -240,7 +238,7 @@ export const examplePlugin = createBackendPlugin({
pluginId: 'example',
register(env) {
env.registerInit({
deps: { config: coreServices.config },
deps: { config: coreServices.rootConfig },
async init({ config }) {
// Here you can read from the current config as you see fit, e.g.:
const value = config.getOptionalString('example.value');
@@ -254,54 +252,3 @@ Before adding custom configuration options, make sure to read [the configuration
docs](../../conf/index.md), in particular the section on [defining configuration
for your own plugins](../../conf/defining.md) which explains how to establish a
configuration schema for your specific plugin.
### Options
You'll have noted that the return values from `createBackendPlugin` and
`createBackendModule` are actually factory functions. These can be made to
accept options that shall be passed in at initialization time.
This pattern can be a good fit for fairly simple, static configuration values.
```ts
export interface ExampleOptions {
silent?: boolean;
}
export const examplePlugin = createBackendPlugin(
(options?: ExampleOptions) => ({
pluginId: 'example',
register(env) {
env.registerInit({
deps: {
// Omitted dependencies but they remain the same as above
},
async init(
{
/* ... */
},
) {
// Here you can access the given options and act accordingly, e.g.:
if (!options?.silent) {
// ...
}
},
});
},
}),
);
```
The return type from `createBackendPlugin` and `createBackendModule` will mimic
this, resulting in a factory function that accepts an optional options object.
You can also make it required to pass in options, by removing the optionality
(the question mark on the options) above.
```ts
backend.add(examplePlugin({ silent: true }));
```
Use this pattern sparingly. There is a big convenience benefit in allowing
people to easily install backend plugins without having to always pass in a
large number of options, and these options cannot easily be made dynamic based
on the environment etc.
@@ -36,8 +36,10 @@ describe('myPlugin', () => {
const fakeConfig = { myPlugin: { value: 7 } };
const { server } = await startTestBackend({
features: [myPlugin()],
services: [mockServices.rootConfig.factory({ data: fakeConfig })],
features: [
myPlugin(),
mockServices.rootConfig.factory({ data: fakeConfig }),
],
});
const response = await request(server).get('/api/example/get-value');
@@ -150,8 +152,10 @@ your test database.
```ts
const { knex, subject } = await createSubject(databaseId);
const { server } = await startTestBackend({
features: [myPlugin()],
services: [[coreServices.database, { getClient: async () => knex }]],
features: [
myPlugin(),
mockServices.database.mock({ getClient: async () => knex }),
],
});
```
@@ -166,3 +170,37 @@ it'll take into account when present.
- `BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING`
- `BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING`
- `BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING`
## Testing Service Factories
To facilitate testing of service factories, the `@backstage/backend-test-utils`
package provides a `ServiceFactoryTester` helper that lets you instantiate services
in a controlled context.
The following example shows how to test a service factory where we also provide
a mocked implementation of the `rootConfig` service.
```ts
import {
mockServices,
ServiceFactoryTester,
} from '@backstage/backend-test-utils';
import { myServiceFactory } from './myServiceFactory.ts';
describe('myServiceFactory', () => {
it('should provide value', async () => {
const fakeConfig = { myConfiguredValue: 7 };
const tester = ServiceFactoryTester.from(myServiceFactory, {
dependencies: [mockServices.rootConfig.factory({ data: fakeConfig })],
});
const myService = await tester.get('test-plugin');
expect(myService.getValue()).toBe(7);
});
});
```
The service factory tester also provides mocked implementations of the majority
of all core services by default.
@@ -51,7 +51,7 @@ export const kubernetesPlugin = createBackendPlugin({
env.registerInit({
deps: {
logger: coreServices.logger,
config: coreServices.config,
config: coreServices.rootConfig,
catalogApi: catalogServiceRef,
discovery: coreServices.discovery,
// The http router service is used to register the router created by the KubernetesBuilder.
+89 -89
View File
@@ -61,13 +61,13 @@ You can configure these additional options by adding an override for the core se
```ts
import { httpRouterServiceFactory } from '@backstage/backend-app-api';
const backend = createBackend({
services: [
httpRouterServiceFactory({
getPath: (pluginId: string) => `/plugins/${pluginId}`,
}),
],
});
const backend = createBackend();
backend.add(
httpRouterServiceFactory({
getPath: (pluginId: string) => `/plugins/${pluginId}`,
}),
);
```
## Root HTTP Router
@@ -118,31 +118,31 @@ You can configure the root HTTP Router service by passing the options to the `cr
```ts
import { rootHttpRouterServiceFactory } from '@backstage/backend-app-api';
const backend = createBackend({
services: [
rootHttpRouterServiceFactory({
configure: ({ app, middleware, routes, config, logger, lifecycle }) => {
// the built in middleware is provided through an option in the configure function
app.use(middleware.helmet());
app.use(middleware.cors());
app.use(middleware.compression());
const backend = createBackend();
// you can add you your own middleware in here
app.use(custom.logging());
backend.add(
rootHttpRouterServiceFactory({
configure: ({ app, middleware, routes, config, logger, lifecycle }) => {
// the built in middleware is provided through an option in the configure function
app.use(middleware.helmet());
app.use(middleware.cors());
app.use(middleware.compression());
// here the routes that are registered by other plugins
app.use(routes);
// you can add you your own middleware in here
app.use(custom.logging());
// some other middleware that comes after the other routes
app.use(middleware.notFound());
app.use(middleware.error());
},
}),
],
});
// here the routes that are registered by other plugins
app.use(routes);
// some other middleware that comes after the other routes
app.use(middleware.notFound());
app.use(middleware.error());
},
}),
);
```
## Config
## Root Config
This service allows you to read configuration values out of your `app-config` YAML files.
@@ -162,7 +162,7 @@ createBackendPlugin({
env.registerInit({
deps: {
log: coreServices.logger,
config: coreServices.config,
config: coreServices.rootConfig,
},
async init({ log, config }) {
const baseUrl = config.getString('backend.baseUrl');
@@ -185,19 +185,19 @@ You can configure these additional options by adding an override for the core se
```ts
import { rootConfigServiceFactory } from '@backstage/backend-app-api';
const backend = createBackend({
services: [
rootConfigServiceFactory({
argv: [
'--config',
'/backstage/app-config.development.yaml',
'--config',
'/backstage/app-config.yaml',
],
remote: { reloadIntervalSeconds: 60 },
}),
],
});
const backend = createBackend();
backend.add(
rootConfigServiceFactory({
argv: [
'--config',
'/backstage/app-config.development.yaml',
'--config',
'/backstage/app-config.yaml',
],
remote: { reloadIntervalSeconds: 60 },
}),
);
```
## Logging
@@ -243,34 +243,34 @@ The following example is how you can override the root logger service to add add
import { coreServices } from '@backstage/backend-plugin-api';
import { WinstonLogger } from '@backstage/backend-app-api';
const backend = createBackend({
services: [
createServiceFactory({
service: coreServices.rootLogger,
deps: {
config: coreServices.config,
},
async factory({ config }) {
const logger = WinstonLogger.create({
meta: {
service: 'backstage',
// here's some additional information that is not part of the
// original implementation
podName: 'myk8spod',
},
level: process.env.LOG_LEVEL || 'info',
format:
process.env.NODE_ENV === 'production'
? format.json()
: WinstonLogger.colorFormat(),
transports: [new transports.Console()],
});
const backend = createBackend();
return logger;
},
}),
],
});
backend.add(
createServiceFactory({
service: coreServices.rootLogger,
deps: {
config: coreServices.rootConfig,
},
async factory({ config }) {
const logger = WinstonLogger.create({
meta: {
service: 'backstage',
// here's some additional information that is not part of the
// original implementation
podName: 'myk8spod',
},
level: process.env.LOG_LEVEL || 'info',
format:
process.env.NODE_ENV === 'production'
? format.json()
: WinstonLogger.colorFormat(),
transports: [new transports.Console()],
});
return logger;
},
}),
);
```
## Cache
@@ -440,14 +440,14 @@ You can configure these additional options by adding an override for the core se
```ts
import { identityServiceFactory } from '@backstage/backend-app-api';
const backend = createBackend({
services: [
identityServiceFactory({
issuer: 'backstage',
algorithms: ['ES256', 'RS256'],
}),
],
});
const backend = createBackend();
backend.add(
identityServiceFactory({
issuer: 'backstage',
algorithms: ['ES256', 'RS256'],
}),
);
```
## Lifecycle
@@ -532,19 +532,19 @@ class MyCustomLifecycleService implements RootLifecycleService {
}
}
const backend = createBackend({
services: [
createServiceFactory({
service: coreServices.rootLifecycle,
deps: {
logger: coreServices.rootLogger,
},
async factory({ logger }) {
return new MyCustomLifecycleService(logger);
},
}),
],
});
const backend = createBackend();
backend.add(
createServiceFactory({
service: coreServices.rootLifecycle,
deps: {
logger: coreServices.rootLogger,
},
async factory({ logger }) {
return new MyCustomLifecycleService(logger);
},
}),
);
```
## Permissions
@@ -467,7 +467,7 @@ The next step is to create a custom processor for your new entity kind. This
will be used within the catalog to make sure that it's able to ingest and
validate entities of our new kind. Just like with the definition package, you
can find inspiration in for example the existing
[ScaffolderEntitiesProcessor](https://github.com/backstage/backstage/tree/master/plugins/scaffolder-backend/src/processor/ScaffolderEntitiesProcessor.ts).
[ScaffolderEntitiesProcessor](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend-module-scaffolder-entity-model/src/processor/ScaffolderEntitiesProcessor.ts).
We also provide a high-level example of what a catalog process for a custom
entity might look like:
@@ -627,3 +627,98 @@ output things. You can grab that output using `steps.$stepId.output.$property`.
You can read more about all the `inputs` and `outputs` defined in the actions in
code part of the `JSONSchema`, or you can read more about our
[built in actions](./builtin-actions.md).
## Built in Filters
Template filters are functions that help you transform data, extract specific information,
and perform various operations in Scaffolder Templates.
This section introduces the built-in filters provided by Backstage and offers examples of
how to use them in the Scaffolder templates. It's important to mention that Backstage also leverages the
native filters from the Nunjucks library. For a complete list of these native filters and their usage,
refer to the [Nunjucks documentation](https://mozilla.github.io/nunjucks/templating.html#builtin-filters).
### parseRepoUrl
The `parseRepoUrl` filter parse a repository URL into
its components, such as `owner`, repository `name`, and more.
**Usage Example:**
```yaml
- id: log
name: Parse Repo URL
action: debug:log
input:
extra: ${{ parameters.repoUrl | parseRepoUrl }}
```
- **Input**: `github.com?repo=backstage&org=backstage`
- **Output**: [RepoSpec](https://github.com/backstage/backstage/blob/v1.17.2/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts#L39)
### parseEntityRef
The `parseEntityRef` filter allows you to extract different parts of
an entity reference, such as the `kind`, `namespace`, and `name`.
**Usage example**
1. Without context
```yaml
- id: log
name: Parse Entity Reference
action: debug:log
input:
extra: ${{ parameters.owner | parseEntityRef }}
```
- **Input**: `group:techdocs`
- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23)
2. With context
```yaml
- id: log
name: Parse Entity Reference
action: debug:log
input:
extra: ${{ parameters.owner | parseEntityRef({ defaultKind:"group", defaultNamespace:"another-namespace" }) }}
```
- **Input**: `techdocs`
- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23)
### pick
This `pick` filter allows you to select specific properties from an object.
**Usage Example**
```yaml
- id: log
name: Pick
action: debug:log
input:
extra: ${{ parameters.owner | parseEntityRef | pick('name') }}
```
- **Input**: `{ kind: 'Group', namespace: 'default', name: 'techdocs' }`
- **Output**: `techdocs`
### projectSlug
The `projectSlug` filter generates a project slug from a repository URL
**Usage Example**
```yaml
- id: log
name: Project Slug
action: debug:log
input:
extra: ${{ parameters.repoUrl | projectSlug }}
```
- **Input**: `github.com?repo=backstage&org=backstage`
- **Output**: `backstage/backstage`
+4 -222
View File
@@ -5,228 +5,10 @@ title: Contributors
description: Documentation on how to get set up for doing development on the Backstage repository
---
This section describes how to get set up for doing development on the Backstage
repository.
Our vision for Backstage is for it to become the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We cant do it alone.
## Cloning the Repository
Therefore we want to create a strong community of contributors -- all working together to create the kind of delightful experience that our developers deserve.
Ok. So you're gonna want some code right? Go ahead and fork the repository into
your own GitHub account and clone that code to your local machine or you can
grab the one for the origin like so:
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. ❤️
```bash
git clone git@github.com:backstage/backstage --depth 1
```
If you cloned a fork, you can add the upstream dependency like so:
```bash
git remote add upstream git@github.com:backstage/backstage
git pull upstream master
```
After you have cloned the Backstage repository, you should run the following
commands once to set things up for development:
```bash
$ cd backstage # change to root directory of project
$ yarn install # fetch dependency packages - may take a while
$ yarn tsc # does a first run of type generation and checks
```
## Serving the Example App
Open a terminal window and start the web app by using the following command from
the project root. Make sure you have run the above mentioned commands first.
```bash
$ yarn dev
```
This is going to start two things, the frontend (:3000) and the backend (:7007).
This should open a local instance of Backstage in your browser, otherwise open
one of the URLs printed in the terminal.
By default, Backstage will start on port 3000, however you can override this by
setting an environment variable `PORT` on your local machine. e.g.
`export PORT=8080` then running `yarn start`. Or `PORT=8080 yarn start`.
Once successfully started, you should see the following message in your terminal
window:
```sh
$ concurrently "yarn start" "yarn start-backend"
$ yarn workspace example-app start
$ yarn workspace example-backend start
$ backstage-cli app:serve
$ backstage-cli backend:dev
[0] Loaded config from app-config.yaml
[1] Build succeeded
[0] 「wds」: Project is running at http://localhost:3000/
[0] 「wds」: webpack output is served from /
[0] 「wds」: Content not from webpack is served from $BACKSTAGE_DIR/packages/app/public
[0] 「wds」: 404s will fallback to /index.html
[0] 「wdm」: wait until bundle finished: /
[1] 2021-02-12T20:58:17.614Z backstage info Loaded config from app-config.yaml
```
You'll see how you get both logs for the frontend `webpack-dev-server` which
serves the react app ([0]) and the backend ([1]);
Visit http://localhost:3000 and you should see the bleeding edge of Backstage
ready for contributions!
## Editor
The Backstage development environment does not require any specific editor, but
it is intended to be used with one that has built-in linting and type-checking.
The development server does not include any checks by default, but they can be
enabled using the `--check` flag. Note that using the flag may consume more
system resources and slow things down.
## Package Scripts
There are many commands to be found in the root
[package.json](https://github.com/backstage/backstage/blob/master/package.json),
here are some useful ones:
```python
yarn start # Start serving the example app, use --check to include type checks and linting
yarn storybook # Start local storybook, useful for working on components in @backstage/core-components
yarn workspace @backstage/plugin-api-docs start # Serve api-docs plugin only, also supports --check
yarn tsc # Run typecheck, use --watch for watch mode
yarn tsc:full # Run full type checking, for example without skipLibCheck, use in CI
yarn build:backend # Build the backend package, depends on tsc
yarn build:all # Build published versions of packages, depends on tsc
yarn lint # lint packages that have changed since later commit on origin/master
yarn lint:all # lint all packages
yarn lint:type-deps # verify that @types/* dependencies are placed correctly in packages
yarn test # test packages that have changed since later commit on origin/master
yarn test:all # test all packages
yarn clean # Remove all output folders and @backstage/cli cache
yarn diff # Make sure all plugins are up to date with the latest plugin template
yarn new # Create a new module
```
> See
> [package.json](https://github.com/backstage/backstage/blob/master/package.json)
> for other yarn commands/options.
## Local configuration
Backstage allows you to specify the configuration used while running the
application on your computer. Local configuration is read from
`app-config.local.yaml`. This file is ignored by Git, which means that you can
safely use it to reference secrets like GitHub tokens without worrying about
these secrets, inadvertently ending up in the Git repository. You do not need to
copy everything from the default config to the local config.
`app-config.local.yaml` will be merged with `app-config.yaml` and overwrite the
default app configs.
> NOTE: If you want to add your own configuration values to access in the
> frontend you also need to mark those values as visible using configuration
> schema, either in the app or in your own plugin. For more information, see
> [Defining Configuration](../conf/defining.md).
You can learn more about the local configuration in
[Static Configuration in Backstage](../conf/) section.
## Writing changesets
Changesets are an important part of the development process. They are used to
generate Changelog entries for all changes to the project. Ultimately they are
read by the end users to learn about important changes and fixes to the project.
Some of these fixes might require manual intervention from users so it's
important to write changesets that users understand and can take action on.
Here are some important do's and don'ts when writing changesets:
### Changeset should give a clear description to what has changed
#### Bad
```
---
'@backstage/catalog': patch
---
Fixed table layout
```
#### Good
```
---
'@backstage/catalog': patch
---
Fixed bug in EntityTable component where table layout did not readjust properly below 1080x768 pixels.
```
### Breaking changes not caught by the type checker should be clearly marked with bold **BREAKING** text
#### Bad
```
---
'@backstage/catalog': minor
---
getEntity is now a function that returns a Promise.
```
#### Good
```
---
'@backstage/catalog': minor
---
**BREAKING** The getEntity function now returns a Promise and **must** be awaited from now on.
```
### Changes to code should include a diff of the files that need updating
#### Bad
```
---
'@backstage/catalog': patch
---
**BREAKING** The catalogEngine now requires a flux capacitor to be passed.
```
#### Good
---
'@backstage/catalog': patch
---
**BREAKING** The catalog createRouter now requires that a `FluxCapacitor` is
passed to the router.
These changes are **required** to `packages/backend/src/plugins/catalog.ts`
```diff
+ import { FluxCapacitor } from '@backstage/time';
+ const fluxCapacitor = new FluxCapacitor();
return await createRouter({
entitiesCatalog,
locationAnalyzer,
locationService,
+ fluxCapacitor,
logger: env.logger,
config: env.config,
});
```
To get you started we've put together a [Contributors Guide in the Backstage GitHub repo](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) that has all the information you need
+1 -1
View File
@@ -11,7 +11,7 @@ A Backstage App is a monorepo setup with `lerna` that includes everything you
need to run Backstage in your own environment.
If you intend to contribute a plugin, new feature, or bug fix to the Backstage project, you
may want to read the [Contributors](./contributors.md) guide instead.
may want to read the [Contributors](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) guide instead.
## Create an app
+1 -1
View File
@@ -15,7 +15,7 @@ system using tools like apt-get, npm, yarn, curl. Docker knowledge is also
helpful for making the best use of your Backstage installation.
If you are planning to contribute plugins or to the project in general, we advise
you to use the [Contributors](contributors.md) guide to do a repository-based installation.
you to use the [Contributors](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) guide to do a repository-based installation.
### Prerequisites
+42 -1
View File
@@ -65,11 +65,52 @@ catalog:
yourProviderId:
host: gitlab.com
orgEnabled: true
group: org/teams # Optional. Must not end with slash. Accepts only groups under the provided path (which will be stripped)
group: org/teams # Required for gitlab.com when `orgEnabled: true`. Optional for self managed. Must not end with slash. Accepts only groups under the provided path (which will be stripped)
groupPattern: '[\s\S]*' # Optional. Filters found groups based on provided pattern. Defaults to `[\s\S]*`, which means to not filter anything
```
### Groups
When the `group` parameter is provided, the corresponding path prefix will be
stripped out from each matching group when computing the unique entity name.
e.g. If `group` is `org/teams`, the name for `org/teams/avengers/gotg` will be
`avengers-gotg`.
For gitlab.com, when `orgEnabled: true`, the `group` parameter is required in
order to limit the ingestion to a group within your organisation. `Group`
entities will only be ingested for the configured group, or its descendant groups,
but not any ancestor groups higher than the configured group path. Only groups
which contain members will be ingested.
### Users
For self hosted, all `User` entities are ingested from the entire instance.
For gitlab.com `User` entities for users who have [direct or inherited membership](https://docs.gitlab.com/ee/user/project/members/index.html#membership-types)
of the top-level group for the configured group path will be ingested.
### Limiting `User` and `Group` entity ingestion in the provider
Optionally, you can limit the entity types ingested by the provider when using
`orgEnabled: true` with the following `rules` configuration to limit it to only
`User` and `Group` entities.
```yaml
catalog:
providers:
gitlab:
yourOrgDataProviderId:
host: gitlab.com
orgEnabled: true
group: org/teams
rules:
- allow: [Group, User]
```
## Troubleshooting
**NOTE**: If any groups that are being ingested are empty groups (i.e. do not
contain any projects) and the user which provisioned the token is shared with a
higher level group via [group sharing](https://docs.gitlab.com/ee/user/group/manage.html#share-a-group-with-another-group)
and you don't see the expected number of `Group` entities in the catalog you may
be hitting this [Gitlab issue](https://gitlab.com/gitlab-org/gitlab/-/issues/267996).
+1 -1
View File
@@ -62,7 +62,7 @@ steps:
- **Bundling** - Combines a package and all of its dependencies into a
production-ready bundle
These steps are generally kept isolated form each other, with each step focusing
These steps are generally kept isolated from each other, with each step focusing
on its specific task. For example, we do not do linting or type checking
together with the building or bundling. This is so that we can provide more
flexibility and avoid duplicate work, improving performance. It is strongly
+4 -6
View File
@@ -266,12 +266,10 @@ class GoogleCloudLogger implements LoggerService {
}
// packages/backend/src/index.ts
const backend = createBackend({
services: [
// supplies additional or replacement services to the backend
GoogleCloudLogger.factory(),
],
});
const backend = createBackend();
// supplies additional or replacement services to the backend
backend.add(GoogleCloudLogger.factory());
```
## Testing
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,9 @@
---
title: Search extension for Azure Cognitive Search
author: AP Communications
authorUrl: https://www.ap-com.co.jp/en/
category: Search
description: Search extension plugin to create backstage search indexes into Azure Cognitive Search.
documentation: https://github.com/ap-communications/platt-backstage-plugin/tree/main/plugins/search-backend-module-cognitive-search
npmPackageName: '@platt/plugin-search-backend-module-cognitive-search'
addedDate: '2023-09-13'
@@ -0,0 +1,15 @@
---
title: Veecode GitHub Workflows
author: Veecode Platform
authorUrl: https://github.com/veecode-platform
category: CI/CD
description: The Github Workflows plugin provides an alternative for manually triggering GitHub workflows from within your Backstage component.
documentation: https://github.com/veecode-platform/platform-backstage-plugins/tree/master/plugins/github-workflows
iconUrl: https://veecode-platform.github.io/support/imgs/logo_2.svg
npmPackageName: '@veecode-platform/backstage-plugin-github-workflows'
tags:
- ci
- cd
- github
- workflows
addedDate: '2023-09-12'
+43 -43
View File
@@ -2604,90 +2604,90 @@ __metadata:
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-darwin-arm64@npm:1.3.82"
"@swc/core-darwin-arm64@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-darwin-arm64@npm:1.3.84"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-x64@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-darwin-x64@npm:1.3.82"
"@swc/core-darwin-x64@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-darwin-x64@npm:1.3.84"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@swc/core-linux-arm-gnueabihf@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.82"
"@swc/core-linux-arm-gnueabihf@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.84"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@swc/core-linux-arm64-gnu@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.82"
"@swc/core-linux-arm64-gnu@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-linux-arm64-gnu@npm:1.3.84"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-arm64-musl@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-linux-arm64-musl@npm:1.3.82"
"@swc/core-linux-arm64-musl@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-linux-arm64-musl@npm:1.3.84"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@swc/core-linux-x64-gnu@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-linux-x64-gnu@npm:1.3.82"
"@swc/core-linux-x64-gnu@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-linux-x64-gnu@npm:1.3.84"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@swc/core-linux-x64-musl@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-linux-x64-musl@npm:1.3.82"
"@swc/core-linux-x64-musl@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-linux-x64-musl@npm:1.3.84"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@swc/core-win32-arm64-msvc@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.82"
"@swc/core-win32-arm64-msvc@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-win32-arm64-msvc@npm:1.3.84"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@swc/core-win32-ia32-msvc@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.82"
"@swc/core-win32-ia32-msvc@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-win32-ia32-msvc@npm:1.3.84"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@swc/core-win32-x64-msvc@npm:1.3.82":
version: 1.3.82
resolution: "@swc/core-win32-x64-msvc@npm:1.3.82"
"@swc/core-win32-x64-msvc@npm:1.3.84":
version: 1.3.84
resolution: "@swc/core-win32-x64-msvc@npm:1.3.84"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@swc/core@npm:^1.3.46":
version: 1.3.82
resolution: "@swc/core@npm:1.3.82"
version: 1.3.84
resolution: "@swc/core@npm:1.3.84"
dependencies:
"@swc/core-darwin-arm64": 1.3.82
"@swc/core-darwin-x64": 1.3.82
"@swc/core-linux-arm-gnueabihf": 1.3.82
"@swc/core-linux-arm64-gnu": 1.3.82
"@swc/core-linux-arm64-musl": 1.3.82
"@swc/core-linux-x64-gnu": 1.3.82
"@swc/core-linux-x64-musl": 1.3.82
"@swc/core-win32-arm64-msvc": 1.3.82
"@swc/core-win32-ia32-msvc": 1.3.82
"@swc/core-win32-x64-msvc": 1.3.82
"@swc/core-darwin-arm64": 1.3.84
"@swc/core-darwin-x64": 1.3.84
"@swc/core-linux-arm-gnueabihf": 1.3.84
"@swc/core-linux-arm64-gnu": 1.3.84
"@swc/core-linux-arm64-musl": 1.3.84
"@swc/core-linux-x64-gnu": 1.3.84
"@swc/core-linux-x64-musl": 1.3.84
"@swc/core-win32-arm64-msvc": 1.3.84
"@swc/core-win32-ia32-msvc": 1.3.84
"@swc/core-win32-x64-msvc": 1.3.84
"@swc/types": ^0.1.4
peerDependencies:
"@swc/helpers": ^0.5.0
@@ -2715,7 +2715,7 @@ __metadata:
peerDependenciesMeta:
"@swc/helpers":
optional: true
checksum: 4c4adbed22071c0af935129a6fe7cc35d1688a5c1a069efdd8c97740930fa7eaf324e75dc88634a4ea0520330f6c88101d5aa5cf7ed6e02d336cc46ee7131ea3
checksum: dee45823923c29dde579ed1121c4392c937826d575c87f62399ba7a0b27cacfeb05da97b65cf49a721a50127bb1e22ca5c07defa784ec2a47fed33e3498ef1b9
languageName: node
linkType: hard
-1
View File
@@ -28,7 +28,6 @@ nav:
- Customize the look-and-feel of your App: 'getting-started/app-custom-theme.md'
- Keeping Backstage Updated: 'getting-started/keeping-backstage-updated.md'
- Key Concepts: 'getting-started/concepts.md'
- Contributors: 'getting-started/contributors.md'
- Project Structure: 'getting-started/project-structure.md'
- Local Development:
- CLI:
+8 -1
View File
@@ -48,7 +48,7 @@
"@types/react": "^17",
"@types/react-dom": "^17"
},
"version": "1.18.0-next.2",
"version": "1.18.0-next.3",
"dependencies": {
"@backstage/errors": "workspace:^",
"@manypkg/get-packages": "^1.1.3"
@@ -96,6 +96,13 @@
"*.md": [
"node ./scripts/check-docs-quality"
],
"{plugins,packages}/*/catalog-info.yaml": [
"yarn backstage-repo-tools generate-catalog-info --ci"
],
"{.github/CODEOWNERS,package.json}": [
"yarn backstage-repo-tools generate-catalog-info",
"git add */catalog-info.yaml"
],
"./yarn.lock": [
"node ./scripts/verify-lockfile-duplicates --fix"
],
+12
View File
@@ -1,5 +1,17 @@
# @backstage/app-defaults
## 1.4.3-next.3
### Patch Changes
- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds.
- Updated dependencies
- @backstage/core-app-api@1.10.0-next.3
- @backstage/core-components@0.13.5-next.3
- @backstage/core-plugin-api@1.6.0-next.3
- @backstage/plugin-permission-react@0.4.15-next.3
- @backstage/theme@0.4.2-next.0
## 1.4.3-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/app-defaults",
"description": "Provides the default wiring of a Backstage App",
"version": "1.4.3-next.2",
"version": "1.4.3-next.3",
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -0,0 +1,9 @@
# app-next-example-plugin
## 0.0.1-next.0
### Patch Changes
- Updated dependencies
- @backstage/frontend-plugin-api@0.1.0-next.0
- @backstage/core-components@0.13.5-next.3
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: app-next-example-plugin
title: app-next-example-plugin
description: Backstage internal example plugin
spec:
lifecycle: experimental
type: backstage-frontend-plugin
owner: maintainers
@@ -1,7 +1,7 @@
{
"name": "app-next-example-plugin",
"description": "Backstage internal example plugin",
"version": "0.0.0",
"version": "0.0.1-next.0",
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
@@ -23,7 +23,7 @@ import {
export const ExamplePage = createPageExtension({
id: 'example.page',
defaultPath: '/example',
component: () => import('./Component').then(m => <m.Component />),
loader: () => import('./Component').then(m => <m.Component />),
});
/** @public */
+77
View File
@@ -1,5 +1,82 @@
# example-app-next
## 0.0.1-next.2
### Patch Changes
- Updated dependencies
- @backstage/plugin-home@0.5.8-next.3
- @backstage/frontend-plugin-api@0.1.0-next.0
- @backstage/frontend-app-api@0.1.0-next.0
- @backstage/plugin-graphiql@0.2.54-next.3
- @backstage/cli@0.22.13-next.3
- @backstage/plugin-kubernetes@0.10.3-next.3
- @backstage/app-defaults@1.4.3-next.3
- @backstage/catalog-model@1.4.2-next.2
- @backstage/config@1.1.0-next.2
- @backstage/core-app-api@1.10.0-next.3
- @backstage/core-components@0.13.5-next.3
- @backstage/core-plugin-api@1.6.0-next.3
- @backstage/integration-react@1.1.19-next.3
- @backstage/plugin-adr@0.6.7-next.3
- @backstage/plugin-airbrake@0.3.24-next.3
- @backstage/plugin-apache-airflow@0.2.15-next.3
- @backstage/plugin-api-docs@0.9.11-next.3
- @backstage/plugin-azure-devops@0.3.6-next.3
- @backstage/plugin-azure-sites@0.1.13-next.3
- @backstage/plugin-badges@0.2.48-next.3
- @backstage/plugin-catalog-common@1.0.16-next.2
- @backstage/plugin-catalog-graph@0.2.36-next.3
- @backstage/plugin-catalog-import@0.9.14-next.3
- @backstage/plugin-catalog-react@1.8.4-next.3
- @backstage/plugin-catalog-unprocessed-entities@0.1.3-next.3
- @backstage/plugin-circleci@0.3.24-next.3
- @backstage/plugin-cloudbuild@0.3.24-next.3
- @backstage/plugin-code-coverage@0.2.17-next.3
- @backstage/plugin-cost-insights@0.12.13-next.3
- @backstage/plugin-devtools@0.1.4-next.3
- @backstage/plugin-dynatrace@7.0.4-next.3
- @backstage/plugin-entity-feedback@0.2.7-next.3
- @backstage/plugin-explore@0.4.10-next.3
- @backstage/plugin-gcalendar@0.3.18-next.3
- @backstage/plugin-gcp-projects@0.3.41-next.3
- @backstage/plugin-github-actions@0.6.5-next.3
- @backstage/plugin-gocd@0.1.30-next.3
- @backstage/plugin-jenkins@0.8.6-next.3
- @backstage/plugin-kafka@0.3.24-next.3
- @backstage/plugin-lighthouse@0.4.9-next.3
- @backstage/plugin-linguist@0.1.9-next.3
- @backstage/plugin-linguist-common@0.1.2-next.0
- @backstage/plugin-microsoft-calendar@0.1.7-next.3
- @backstage/plugin-newrelic@0.3.40-next.3
- @backstage/plugin-newrelic-dashboard@0.2.17-next.3
- @backstage/plugin-octopus-deploy@0.2.6-next.3
- @backstage/plugin-org@0.6.14-next.3
- @backstage/plugin-pagerduty@0.6.5-next.3
- @backstage/plugin-permission-react@0.4.15-next.3
- @backstage/plugin-playlist@0.1.16-next.3
- @backstage/plugin-puppetdb@0.1.7-next.3
- @backstage/plugin-rollbar@0.4.24-next.3
- @backstage/plugin-scaffolder@1.15.0-next.3
- @backstage/plugin-scaffolder-react@1.5.5-next.3
- @backstage/plugin-search@1.4.0-next.3
- @backstage/plugin-search-common@1.2.6-next.2
- @backstage/plugin-search-react@1.7.0-next.3
- @backstage/plugin-sentry@0.5.9-next.3
- @backstage/plugin-shortcuts@0.3.14-next.3
- @backstage/plugin-stack-overflow@0.1.20-next.3
- @backstage/plugin-stackstorm@0.1.6-next.3
- @backstage/plugin-tech-insights@0.3.16-next.3
- @backstage/plugin-tech-radar@0.6.8-next.3
- @backstage/plugin-techdocs@1.7.0-next.3
- @backstage/plugin-techdocs-module-addons-contrib@1.1.0-next.3
- @backstage/plugin-techdocs-react@1.1.10-next.3
- @backstage/plugin-todo@0.2.26-next.3
- @backstage/plugin-user-settings@0.7.9-next.3
- @backstage/theme@0.4.2-next.0
- app-next-example-plugin@0.0.1-next.0
- @internal/plugin-catalog-customized@0.0.14-next.3
## 0.0.1-next.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-app-next",
"version": "0.0.1-next.1",
"version": "0.0.1-next.2",
"private": true,
"backstage": {
"role": "frontend"
@@ -36,7 +36,7 @@ const IndexPage = createPageExtension({
id: 'index',
defaultPath: '/',
routeRef: indexRouteRef,
component: async () => {
loader: async () => {
const Component = () => {
const page1Link = useRouteRef(page1RouteRef);
return (
@@ -59,7 +59,7 @@ const Page1 = createPageExtension({
id: 'page1',
defaultPath: '/page1',
routeRef: page1RouteRef,
component: async () => {
loader: async () => {
const Component = () => {
const indexLink = useRouteRef(indexRouteRef);
// const page2Link = useRouteRef(page2RouteRef);
+75
View File
@@ -1,5 +1,80 @@
# example-app
## 0.2.87-next.3
### Patch Changes
- Updated dependencies
- @backstage/plugin-home@0.5.8-next.3
- @backstage/plugin-graphiql@0.2.54-next.3
- @backstage/cli@0.22.13-next.3
- @backstage/plugin-kubernetes@0.10.3-next.3
- @backstage/app-defaults@1.4.3-next.3
- @backstage/catalog-model@1.4.2-next.2
- @backstage/config@1.1.0-next.2
- @backstage/core-app-api@1.10.0-next.3
- @backstage/core-components@0.13.5-next.3
- @backstage/core-plugin-api@1.6.0-next.3
- @backstage/integration-react@1.1.19-next.3
- @backstage/plugin-adr@0.6.7-next.3
- @backstage/plugin-airbrake@0.3.24-next.3
- @backstage/plugin-apache-airflow@0.2.15-next.3
- @backstage/plugin-api-docs@0.9.11-next.3
- @backstage/plugin-azure-devops@0.3.6-next.3
- @backstage/plugin-azure-sites@0.1.13-next.3
- @backstage/plugin-badges@0.2.48-next.3
- @backstage/plugin-catalog-common@1.0.16-next.2
- @backstage/plugin-catalog-graph@0.2.36-next.3
- @backstage/plugin-catalog-import@0.9.14-next.3
- @backstage/plugin-catalog-react@1.8.4-next.3
- @backstage/plugin-catalog-unprocessed-entities@0.1.3-next.3
- @backstage/plugin-circleci@0.3.24-next.3
- @backstage/plugin-cloudbuild@0.3.24-next.3
- @backstage/plugin-code-coverage@0.2.17-next.3
- @backstage/plugin-cost-insights@0.12.13-next.3
- @backstage/plugin-devtools@0.1.4-next.3
- @backstage/plugin-dynatrace@7.0.4-next.3
- @backstage/plugin-entity-feedback@0.2.7-next.3
- @backstage/plugin-explore@0.4.10-next.3
- @backstage/plugin-gcalendar@0.3.18-next.3
- @backstage/plugin-gcp-projects@0.3.41-next.3
- @backstage/plugin-github-actions@0.6.5-next.3
- @backstage/plugin-gocd@0.1.30-next.3
- @backstage/plugin-jenkins@0.8.6-next.3
- @backstage/plugin-kafka@0.3.24-next.3
- @backstage/plugin-lighthouse@0.4.9-next.3
- @backstage/plugin-linguist@0.1.9-next.3
- @backstage/plugin-linguist-common@0.1.2-next.0
- @backstage/plugin-microsoft-calendar@0.1.7-next.3
- @backstage/plugin-newrelic@0.3.40-next.3
- @backstage/plugin-newrelic-dashboard@0.2.17-next.3
- @backstage/plugin-nomad@0.1.5-next.3
- @backstage/plugin-octopus-deploy@0.2.6-next.3
- @backstage/plugin-org@0.6.14-next.3
- @backstage/plugin-pagerduty@0.6.5-next.3
- @backstage/plugin-permission-react@0.4.15-next.3
- @backstage/plugin-playlist@0.1.16-next.3
- @backstage/plugin-puppetdb@0.1.7-next.3
- @backstage/plugin-rollbar@0.4.24-next.3
- @backstage/plugin-scaffolder@1.15.0-next.3
- @backstage/plugin-scaffolder-react@1.5.5-next.3
- @backstage/plugin-search@1.4.0-next.3
- @backstage/plugin-search-common@1.2.6-next.2
- @backstage/plugin-search-react@1.7.0-next.3
- @backstage/plugin-sentry@0.5.9-next.3
- @backstage/plugin-shortcuts@0.3.14-next.3
- @backstage/plugin-stack-overflow@0.1.20-next.3
- @backstage/plugin-stackstorm@0.1.6-next.3
- @backstage/plugin-tech-insights@0.3.16-next.3
- @backstage/plugin-tech-radar@0.6.8-next.3
- @backstage/plugin-techdocs@1.7.0-next.3
- @backstage/plugin-techdocs-module-addons-contrib@1.1.0-next.3
- @backstage/plugin-techdocs-react@1.1.10-next.3
- @backstage/plugin-todo@0.2.26-next.3
- @backstage/plugin-user-settings@0.7.9-next.3
- @backstage/theme@0.4.2-next.0
- @internal/plugin-catalog-customized@0.0.14-next.3
## 0.2.87-next.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-app",
"version": "0.2.87-next.2",
"version": "0.2.87-next.3",
"private": true,
"backstage": {
"role": "frontend"
+20
View File
@@ -1,5 +1,25 @@
# @backstage/backend-app-api
## 0.5.3-next.3
### Patch Changes
- 154632d8753b: Add support for discovering additional service factories during startup.
- cb7fc410ed99: The experimental backend feature discovery now only considers default exports from packages. It no longer filters packages to include based on the package role, except that `'cli'` packages are ignored. However, the `"backstage"` field is still required in `package.json`.
- 3b30b179cb38: Add support for installing features as package imports, for example `backend.add(import('my-plugin'))`.
- Updated dependencies
- @backstage/config@1.1.0-next.2
- @backstage/errors@1.2.2-next.0
- @backstage/types@1.1.1-next.0
- @backstage/plugin-permission-node@0.7.14-next.3
- @backstage/backend-plugin-api@0.6.3-next.3
- @backstage/backend-common@0.19.5-next.3
- @backstage/backend-tasks@0.5.8-next.3
- @backstage/cli-common@0.1.12
- @backstage/cli-node@0.1.4-next.0
- @backstage/config-loader@1.5.0-next.3
- @backstage/plugin-auth-node@0.3.0-next.3
## 0.5.3-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-app-api",
"description": "Core API used by Backstage backend apps",
"version": "0.5.3-next.2",
"version": "0.5.3-next.3",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
+16
View File
@@ -1,5 +1,21 @@
# @backstage/backend-common
## 0.19.5-next.3
### Patch Changes
- Updated dependencies
- @backstage/backend-app-api@0.5.3-next.3
- @backstage/config@1.1.0-next.2
- @backstage/errors@1.2.2-next.0
- @backstage/integration@1.7.0-next.3
- @backstage/types@1.1.1-next.0
- @backstage/backend-plugin-api@0.6.3-next.3
- @backstage/backend-dev-utils@0.1.1
- @backstage/cli-common@0.1.12
- @backstage/config-loader@1.5.0-next.3
- @backstage/integration-aws-node@0.1.6-next.2
## 0.19.5-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-common",
"description": "Common functionality library for Backstage backends",
"version": "0.19.5-next.2",
"version": "0.19.5-next.3",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
+9
View File
@@ -1,5 +1,14 @@
# @backstage/backend-defaults
## 0.2.3-next.3
### Patch Changes
- Updated dependencies
- @backstage/backend-app-api@0.5.3-next.3
- @backstage/backend-plugin-api@0.6.3-next.3
- @backstage/backend-common@0.19.5-next.3
## 0.2.3-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-defaults",
"description": "Backend defaults used by Backstage backend apps",
"version": "0.2.3-next.2",
"version": "0.2.3-next.3",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
+35
View File
@@ -1,5 +1,40 @@
# example-backend-next
## 0.0.15-next.3
### Patch Changes
- Updated dependencies
- @backstage/plugin-techdocs-backend@1.7.0-next.3
- @backstage/plugin-proxy-backend@0.4.0-next.3
- @backstage/plugin-adr-backend@0.4.0-next.3
- @backstage/plugin-azure-devops-backend@0.4.0-next.3
- @backstage/plugin-badges-backend@0.3.0-next.3
- @backstage/plugin-catalog-backend-module-unprocessed@0.3.0-next.3
- @backstage/plugin-devtools-backend@0.2.0-next.3
- @backstage/plugin-entity-feedback-backend@0.2.0-next.3
- @backstage/plugin-lighthouse-backend@0.3.0-next.3
- @backstage/plugin-linguist-backend@0.5.0-next.3
- @backstage/plugin-todo-backend@0.3.0-next.3
- @backstage/plugin-app-backend@0.3.51-next.3
- @backstage/plugin-catalog-backend@1.13.0-next.3
- @backstage/plugin-kubernetes-backend@0.11.6-next.3
- @backstage/plugin-permission-backend@0.5.26-next.3
- @backstage/plugin-permission-backend-module-allow-all-policy@0.1.0-next.1
- @backstage/plugin-scaffolder-backend@1.17.0-next.3
- @backstage/plugin-search-backend@1.4.3-next.3
- @backstage/plugin-search-backend-module-catalog@0.1.7-next.3
- @backstage/plugin-search-backend-module-explore@0.1.7-next.3
- @backstage/plugin-search-backend-module-techdocs@0.1.7-next.3
- @backstage/plugin-permission-common@0.7.8-next.2
- @backstage/plugin-permission-node@0.7.14-next.3
- @backstage/backend-plugin-api@0.6.3-next.3
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.0-next.0
- @backstage/backend-defaults@0.2.3-next.3
- @backstage/backend-tasks@0.5.8-next.3
- @backstage/plugin-auth-node@0.3.0-next.3
- @backstage/plugin-search-backend-node@1.2.7-next.3
## 0.0.15-next.2
### Patch Changes
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-backend-next",
"version": "0.0.15-next.2",
"version": "0.0.15-next.3",
"main": "dist/index.cjs.js",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -34,6 +34,7 @@
"@backstage/plugin-azure-devops-backend": "workspace:^",
"@backstage/plugin-badges-backend": "workspace:^",
"@backstage/plugin-catalog-backend": "workspace:^",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "workspace:^",
"@backstage/plugin-catalog-backend-module-unprocessed": "workspace:^",
"@backstage/plugin-devtools-backend": "workspace:^",
"@backstage/plugin-entity-feedback-backend": "workspace:^",
+3
View File
@@ -23,6 +23,9 @@ backend.add(import('@backstage/plugin-app-backend/alpha'));
backend.add(import('@backstage/plugin-azure-devops-backend'));
backend.add(import('@backstage/plugin-badges-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-unprocessed'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
backend.add(import('@backstage/plugin-devtools-backend'));
backend.add(import('@backstage/plugin-entity-feedback-backend'));
@@ -1,5 +1,12 @@
# @backstage/backend-openapi-utils
## 0.0.4-next.0
### Patch Changes
- Updated dependencies
- @backstage/errors@1.2.2-next.0
## 0.0.3
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-openapi-utils",
"description": "OpenAPI typescript support.",
"version": "0.0.3",
"version": "0.0.4-next.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
+13
View File
@@ -1,5 +1,18 @@
# @backstage/backend-plugin-api
## 0.6.3-next.3
### Patch Changes
- ba4506076e2d: Ensure that root scoped services cannot accept (at a type level) plugin scoped deps
- 474b792d6a43: Service factory functions are now marked as feature factories that can be installed in the backend.
- Updated dependencies
- @backstage/config@1.1.0-next.2
- @backstage/plugin-permission-common@0.7.8-next.2
- @backstage/types@1.1.1-next.0
- @backstage/backend-tasks@0.5.8-next.3
- @backstage/plugin-auth-node@0.3.0-next.3
## 0.6.3-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-plugin-api",
"description": "Core API used by Backstage backend plugins",
"version": "0.6.3-next.2",
"version": "0.6.3-next.3",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
@@ -1,5 +1,28 @@
# @backstage/backend-plugin-manager
## 0.0.1-next.1
### Patch Changes
- Updated dependencies
- @backstage/plugin-catalog-backend@1.13.0-next.3
- @backstage/plugin-events-backend@0.2.12-next.3
- @backstage/config@1.1.0-next.2
- @backstage/errors@1.2.2-next.0
- @backstage/plugin-permission-common@0.7.8-next.2
- @backstage/plugin-search-common@1.2.6-next.2
- @backstage/types@1.1.1-next.0
- @backstage/plugin-permission-node@0.7.14-next.3
- @backstage/backend-plugin-api@0.6.3-next.3
- @backstage/backend-common@0.19.5-next.3
- @backstage/backend-tasks@0.5.8-next.3
- @backstage/cli-common@0.1.12
- @backstage/cli-node@0.1.4-next.0
- @backstage/plugin-auth-node@0.3.0-next.3
- @backstage/plugin-events-node@0.2.12-next.3
- @backstage/plugin-scaffolder-node@0.2.3-next.3
- @backstage/plugin-search-backend-node@1.2.7-next.3
## 0.0.1-next.0
### Patch Changes
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-backend-plugin-manager
title: '@backstage/backend-plugin-manager'
description: Backstage plugin management backend
spec:
lifecycle: experimental
type: backstage-node-library
owner: maintainers
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-plugin-manager",
"description": "Backstage plugin management backend",
"version": "0.0.1-next.0",
"version": "0.0.1-next.1",
"private": true,
"main": "src/index.ts",
"types": "src/index.ts",
+10
View File
@@ -1,5 +1,15 @@
# @backstage/backend-tasks
## 0.5.8-next.3
### Patch Changes
- Updated dependencies
- @backstage/config@1.1.0-next.2
- @backstage/errors@1.2.2-next.0
- @backstage/types@1.1.1-next.0
- @backstage/backend-common@0.19.5-next.3
## 0.5.8-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-tasks",
"description": "Common distributed task management library for Backstage backends",
"version": "0.5.8-next.2",
"version": "0.5.8-next.3",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
@@ -330,6 +330,48 @@ describe('TaskWorker', () => {
const before = fn1.mock.calls.length;
await promise2;
expect(fn1.mock.calls.length).toBeGreaterThan(before);
await knex.destroy();
},
);
it.each(databases.eachSupportedId())(
'next_run_start_at is always the min between schedule changes, %p',
async databaseId => {
const knex = await databases.init(databaseId);
await migrateBackendTasks(knex);
const fn = jest.fn(
async () => new Promise<void>(resolve => setTimeout(resolve, 50)),
);
const settings: TaskSettingsV2 = {
version: 2,
cadence: '*/15 * * * *',
initialDelayDuration: 'PT2M',
timeoutAfterDuration: 'PT1M',
};
const worker = new TaskWorker('task99', fn, knex, logger);
await worker.persistTask(settings);
const row1 = (await knex<DbTasksRow>(DB_TASKS_TABLE))[0];
const settings2 = {
...settings,
cadence: '*/2 * * * *',
initialDelayDuration: 'PT1M',
};
await worker.persistTask(settings2);
const row2 = (await knex<DbTasksRow>(DB_TASKS_TABLE))[0];
expect(row2.next_run_start_at).not.toStrictEqual(row1.next_run_start_at);
const settings3 = { ...settings };
await worker.persistTask(settings3);
const row3 = (await knex<DbTasksRow>(DB_TASKS_TABLE))[0];
expect(row3.next_run_start_at).toStrictEqual(row2.next_run_start_at);
await knex.destroy();
},
);
});
+24 -2
View File
@@ -192,14 +192,36 @@ export class TaskWorker {
// It's OK if the task already exists; if it does, just replace its
// settings with the new value and start the loop as usual.
const settingsJson = JSON.stringify(settings);
await this.knex<DbTasksRow>(DB_TASKS_TABLE)
.insert({
id: this.taskId,
settings_json: JSON.stringify(settings),
settings_json: settingsJson,
next_run_start_at: startAt,
})
.onConflict('id')
.merge(['settings_json']);
.merge(
this.knex.client.config.client.includes('mysql')
? {
settings_json: settingsJson,
next_run_start_at: this.knex.raw(
`CASE WHEN ?? < ?? THEN ?? ELSE ?? END`,
[startAt, 'next_run_start_at', startAt, 'next_run_start_at'],
),
}
: {
settings_json: this.knex.ref('excluded.settings_json'),
next_run_start_at: this.knex.raw(
`CASE WHEN ?? < ?? THEN ?? ELSE ?? END`,
[
'excluded.next_run_start_at',
`${DB_TASKS_TABLE}.next_run_start_at`,
'excluded.next_run_start_at',
`${DB_TASKS_TABLE}.next_run_start_at`,
],
),
},
);
}
/**
+13
View File
@@ -1,5 +1,18 @@
# @backstage/backend-test-utils
## 0.2.3-next.3
### Patch Changes
- 202e52c5e361: Add support for installing backend features via module imports, for example `startTestBackend({ features: [import('my-plugin')] })`.
- Updated dependencies
- @backstage/backend-app-api@0.5.3-next.3
- @backstage/config@1.1.0-next.2
- @backstage/types@1.1.1-next.0
- @backstage/backend-plugin-api@0.6.3-next.3
- @backstage/backend-common@0.19.5-next.3
- @backstage/plugin-auth-node@0.3.0-next.3
## 0.2.3-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-test-utils",
"description": "Test helpers library for Backstage backends",
"version": "0.2.3-next.2",
"version": "0.2.3-next.3",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
+59
View File
@@ -1,5 +1,64 @@
# example-backend
## 0.2.87-next.3
### Patch Changes
- Updated dependencies
- @backstage/plugin-techdocs-backend@1.7.0-next.3
- @backstage/plugin-proxy-backend@0.4.0-next.3
- @backstage/plugin-adr-backend@0.4.0-next.3
- @backstage/plugin-auth-backend@0.19.0-next.3
- @backstage/plugin-azure-devops-backend@0.4.0-next.3
- @backstage/plugin-badges-backend@0.3.0-next.3
- @backstage/plugin-catalog-backend-module-unprocessed@0.3.0-next.3
- @backstage/plugin-devtools-backend@0.2.0-next.3
- @backstage/plugin-entity-feedback-backend@0.2.0-next.3
- @backstage/plugin-kafka-backend@0.3.0-next.3
- @backstage/plugin-lighthouse-backend@0.3.0-next.3
- @backstage/plugin-linguist-backend@0.5.0-next.3
- @backstage/plugin-todo-backend@0.3.0-next.3
- @backstage/plugin-app-backend@0.3.51-next.3
- @backstage/plugin-catalog-backend@1.13.0-next.3
- @backstage/plugin-events-backend@0.2.12-next.3
- @backstage/plugin-kubernetes-backend@0.11.6-next.3
- @backstage/plugin-permission-backend@0.5.26-next.3
- @backstage/plugin-scaffolder-backend@1.17.0-next.3
- @backstage/plugin-search-backend@1.4.3-next.3
- @backstage/plugin-search-backend-module-catalog@0.1.7-next.3
- @backstage/plugin-search-backend-module-elasticsearch@1.3.6-next.3
- @backstage/plugin-search-backend-module-explore@0.1.7-next.3
- @backstage/plugin-search-backend-module-pg@0.5.12-next.3
- @backstage/plugin-search-backend-module-techdocs@0.1.7-next.3
- @backstage/catalog-client@1.4.4-next.2
- @backstage/catalog-model@1.4.2-next.2
- @backstage/config@1.1.0-next.2
- @backstage/integration@1.7.0-next.3
- @backstage/plugin-permission-common@0.7.8-next.2
- @backstage/plugin-search-common@1.2.6-next.2
- @backstage/plugin-permission-node@0.7.14-next.3
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.0-next.0
- example-app@0.2.87-next.3
- @backstage/backend-common@0.19.5-next.3
- @backstage/plugin-explore-backend@0.0.13-next.3
- @backstage/backend-tasks@0.5.8-next.3
- @backstage/plugin-auth-node@0.3.0-next.3
- @backstage/plugin-azure-sites-backend@0.1.13-next.3
- @backstage/plugin-catalog-node@1.4.4-next.3
- @backstage/plugin-code-coverage-backend@0.2.17-next.3
- @backstage/plugin-events-node@0.2.12-next.3
- @backstage/plugin-graphql-backend@0.1.41-next.3
- @backstage/plugin-jenkins-backend@0.2.6-next.3
- @backstage/plugin-nomad-backend@0.1.5-next.3
- @backstage/plugin-playlist-backend@0.3.7-next.3
- @backstage/plugin-rollbar-backend@0.1.48-next.3
- @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.4-next.3
- @backstage/plugin-scaffolder-backend-module-rails@0.4.20-next.3
- @backstage/plugin-search-backend-node@1.2.7-next.3
- @backstage/plugin-tech-insights-backend@0.5.17-next.3
- @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.35-next.3
- @backstage/plugin-tech-insights-node@0.4.9-next.3
## 0.2.87-next.2
### Patch Changes
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-backend",
"version": "0.2.87-next.2",
"version": "0.2.87-next.3",
"main": "dist/index.cjs.js",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -40,6 +40,7 @@
"@backstage/plugin-azure-sites-backend": "workspace:^",
"@backstage/plugin-badges-backend": "workspace:^",
"@backstage/plugin-catalog-backend": "workspace:^",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "workspace:^",
"@backstage/plugin-catalog-backend-module-unprocessed": "workspace:^",
"@backstage/plugin-catalog-node": "workspace:^",
"@backstage/plugin-code-coverage-backend": "workspace:^",
+2 -2
View File
@@ -15,11 +15,11 @@
*/
import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend';
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model';
import { UnprocessedEntitiesModule } from '@backstage/plugin-catalog-backend-module-unprocessed';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { DemoEventBasedEntityProvider } from './DemoEventBasedEntityProvider';
import { UnprocessedEntitiesModule } from '@backstage/plugin-catalog-backend-module-unprocessed';
export default async function createPlugin(
env: PluginEnvironment,
+9
View File
@@ -1,5 +1,14 @@
# @backstage/catalog-client
## 1.4.4-next.2
### Patch Changes
- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds.
- Updated dependencies
- @backstage/catalog-model@1.4.2-next.2
- @backstage/errors@1.2.2-next.0
## 1.4.4-next.1
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/catalog-client",
"description": "An isomorphic client for the catalog backend",
"version": "1.4.4-next.1",
"version": "1.4.4-next.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
+10
View File
@@ -1,5 +1,15 @@
# @backstage/catalog-model
## 1.4.2-next.2
### Patch Changes
- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds.
- Updated dependencies
- @backstage/config@1.1.0-next.2
- @backstage/errors@1.2.2-next.0
- @backstage/types@1.1.1-next.0
## 1.4.2-next.1
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/catalog-model",
"description": "Types and validators that help describe the model of a Backstage Catalog",
"version": "1.4.2-next.1",
"version": "1.4.2-next.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
+9
View File
@@ -1,5 +1,14 @@
# @backstage/cli-node
## 0.1.4-next.0
### Patch Changes
- Updated dependencies
- @backstage/errors@1.2.2-next.0
- @backstage/types@1.1.1-next.0
- @backstage/cli-common@0.1.12
## 0.1.3
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli-node",
"description": "Node.js library for Backstage CLIs",
"version": "0.1.3",
"version": "0.1.4-next.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
+23
View File
@@ -1,5 +1,28 @@
# @backstage/cli
## 0.22.13-next.3
### Patch Changes
- 4d5eeec52d80: Add ESM loader for the experimental backend start command.
- 3494c502aba7: Added a new `repo fix` command that fixes auto-fixable problems in all packages. Initially the command fixes package export declarations, as well as marks all non-bundled frontend packages as side-effect free. Marking packages as free of side-effects can drastically reduce the Webpack bundle size.
- f36113ca2305: Add experimental support for frontend package discovery.
- a23fce763c6a: Fixed a bug where package exports entry points could not be `.tsx` files.
- 4af4defcc114: When running `version:bump` it will now log duplicates instead of throwing an error
- 956d226eeeee: Add `"sideEffects": false` to `package.json` in frontend package templates. This can be added to existing packages using the new `yarn fix` command.
- cd7331587eb3: Removed the experimental `package fix` command that was used to automatically add dependencies to `package.json`, but has since been replaced by the `no-undeclared-imports` rule from `@backstage/eslint-plugin`.
- Updated dependencies
- @backstage/catalog-model@1.4.2-next.2
- @backstage/config@1.1.0-next.2
- @backstage/errors@1.2.2-next.0
- @backstage/integration@1.7.0-next.3
- @backstage/release-manifests@0.0.10-next.1
- @backstage/types@1.1.1-next.0
- @backstage/cli-common@0.1.12
- @backstage/cli-node@0.1.4-next.0
- @backstage/config-loader@1.5.0-next.3
- @backstage/eslint-plugin@0.1.3
## 0.22.13-next.2
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli",
"description": "CLI for developing Backstage plugins and apps",
"version": "0.22.13-next.2",
"version": "0.22.13-next.3",
"publishConfig": {
"access": "public"
},
-142
View File
@@ -1,142 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { paths } from '../lib/paths';
import { ESLint } from 'eslint';
import { join as joinPath, basename } from 'path';
import fs from 'fs-extra';
import { isChildPath } from '@backstage/cli-common';
import { PackageGraph } from '@backstage/cli-node';
function isTestPath(filePath: string) {
if (!isChildPath(joinPath(paths.targetDir, 'src'), filePath)) {
return true;
}
const name = basename(filePath);
return (
name.startsWith('setupTests.') ||
name.includes('.test.') ||
name.includes('.stories.')
);
}
export async function command() {
const pkgJsonPath = paths.resolveTarget('package.json');
const pkg = await fs.readJson(pkgJsonPath);
if (pkg.workspaces) {
throw new Error(
'Adding dependencies to the workspace root is not supported',
);
}
const packages = await PackageGraph.listTargetPackages();
const localPackageVersions = new Map(
packages.map(p => [p.packageJson.name, p.packageJson.version]),
);
const eslint = new ESLint({
cwd: paths.targetDir,
overrideConfig: {
plugins: ['monorepo'],
rules: {
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
`!${joinPath(paths.targetDir, 'src/**')}`,
joinPath(paths.targetDir, 'src/**/*.test.*'),
joinPath(paths.targetDir, 'src/**/*.stories.*'),
joinPath(paths.targetDir, 'src/setupTests.*'),
],
optionalDependencies: true,
peerDependencies: true,
bundledDependencies: true,
},
],
},
},
extensions: ['jsx', 'ts', 'tsx', 'mjs', 'cjs'],
});
const results = await eslint.lintFiles(['.']);
const addedDeps = new Set<string>();
const addedDevDeps = new Set<string>();
const removedDevDeps = new Set<string>();
for (const result of results) {
for (const message of result.messages) {
// Just in case
if (message.ruleId !== 'import/no-extraneous-dependencies') {
continue;
}
const match = message.message.match(/^'([^']*)' should be listed/);
if (!match) {
continue;
}
const packageName = match[1];
if (!localPackageVersions.has(packageName)) {
continue;
}
if (message.message.endsWith('not devDependencies.')) {
addedDeps.add(packageName);
removedDevDeps.add(packageName);
} else if (isTestPath(result.filePath)) {
addedDevDeps.add(packageName);
} else {
addedDeps.add(packageName);
}
}
}
if (addedDeps.size || addedDevDeps.size || removedDevDeps.size) {
for (const name of addedDeps) {
if (!pkg.dependencies) {
pkg.dependencies = {};
}
pkg.dependencies[name] = `^${localPackageVersions.get(name)}`;
}
for (const name of addedDevDeps) {
if (!pkg.devDependencies) {
pkg.devDependencies = {};
}
pkg.devDependencies[name] = `^${localPackageVersions.get(name)}`;
}
for (const name of removedDevDeps) {
delete pkg.devDependencies[name];
}
if (Object.keys(pkg.devDependencies).length === 0) {
delete pkg.devDependencies;
}
if (pkg.dependencies) {
pkg.dependencies = Object.fromEntries(
Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)),
);
}
if (pkg.devDependencies) {
pkg.devDependencies = Object.fromEntries(
Object.entries(pkg.devDependencies).sort(([a], [b]) =>
a.localeCompare(b),
),
);
}
await fs.writeJson(pkgJsonPath, pkg, { spaces: 2 });
}
}
-6
View File
@@ -168,12 +168,6 @@ export function registerScriptCommand(program: Command) {
.description('Run tests, forwarding args to Jest, defaulting to watch mode')
.action(lazy(() => import('./test').then(m => m.default)));
command
.command('fix', { hidden: true })
.description('Applies automated fixes to the package. [EXPERIMENTAL]')
.option('--deps', 'Only fix monorepo dependencies in package.json')
.action(lazy(() => import('./fix').then(m => m.command)));
command
.command('clean')
.description('Delete cache directories')
+100 -40
View File
@@ -29,6 +29,7 @@ import { YarnInfoInspectData } from '../../lib/versioning/packages';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { NotFoundError } from '@backstage/errors';
import { Lockfile } from '../../lib/versioning/Lockfile';
// Remove log coloring to simplify log matching
jest.mock('chalk', () => ({
@@ -224,7 +225,7 @@ describe('bump', () => {
});
});
it('should bump backstage dependencies but not them installed', async () => {
it('should bump backstage dependencies but not install them', async () => {
mockFs({
'/yarn.lock': lockfileMock,
'/package.json': JSON.stringify({
@@ -453,68 +454,36 @@ describe('bump', () => {
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
worker.use(
rest.get(
'https://versions.backstage.io/v1/releases/1.0.0/manifest.json',
(_, res, ctx) =>
res(
ctx.status(200),
ctx.json({
releaseVersion: '2.0.0',
packages: [
{ name: '@backstage/core', version: '5.0.0' },
{ name: '@backstage/core-api', version: '5.0.0' },
],
}),
),
'https://versions.backstage.io/v1/releases/999.0.1/manifest.json',
(_, res, ctx) => res(ctx.status(404), ctx.json({})),
),
);
const { log: logs } = await withLogCollector(['log'], async () => {
await expect(
bump({ pattern: null, release: '1.0.0' } as unknown as Command),
).rejects.toThrow('Duplicate versions present after package bump');
bump({ pattern: null, release: '999.0.1' } as unknown as Command),
).rejects.toThrow('No release found for 999.0.1 version');
});
expect(logs.filter(Boolean)).toEqual([
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Package info not found, ignoring package @backstage/theme',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core-api',
'Package info not found, ignoring package @backstage/theme',
'Some packages are outdated, updating',
'bumping @backstage/core in a to ^5.0.0',
'bumping @backstage/core in b to ^5.0.0',
'Your project is now at version 2.0.0, which has been written to backstage.json',
'Running yarn install to install new versions',
'⚠️ The following packages may have breaking changes:',
' @backstage/core : 1.0.3 ~> 5.0.0',
' https://github.com/backstage/backstage/blob/master/packages/core/CHANGELOG.md',
'Version bump complete!',
]);
expect(runObj.run).toHaveBeenCalledTimes(1);
expect(runObj.run).toHaveBeenCalledWith(
'yarn',
['install'],
expect.any(Object),
);
expect(runObj.run).toHaveBeenCalledTimes(0);
const packageA = await fs.readJson('/packages/a/package.json');
expect(packageA).toEqual({
name: 'a',
dependencies: {
'@backstage/core': '^5.0.0',
'@backstage/core': '^1.0.5',
},
});
const packageB = await fs.readJson('/packages/b/package.json');
expect(packageB).toEqual({
name: 'b',
dependencies: {
'@backstage/core': '^5.0.0',
'@backstage/core': '^1.0.3',
'@backstage/theme': '^1.0.0',
},
});
expect(await fs.readJson('/backstage.json')).toEqual({ version: '2.0.0' });
});
it('should prefer versions from the highest manifest version when main is not specified', async () => {
@@ -825,6 +794,97 @@ describe('bump', () => {
},
});
});
it('should log duplicates', async () => {
jest.spyOn(Lockfile.prototype, 'analyze').mockReturnValue({
invalidRanges: [],
newVersions: [],
newRanges: [
{
name: 'first-duplicate',
oldRange: 'first-duplicate',
newRange: 'first-duplicate',
oldVersion: '1.0.0',
newVersion: '2.0.0',
},
{
name: 'second-duplicate',
oldRange: 'second-duplicate',
newRange: 'second-duplicate',
oldVersion: '1.0.0',
newVersion: '2.0.0',
},
{
name: 'third-duplicate',
oldRange: 'third-duplicate',
newRange: 'third-duplicate',
oldVersion: '1.0.0',
newVersion: '2.0.0',
},
],
});
mockFs({
'/yarn.lock': lockfileMock,
'/package.json': JSON.stringify({
workspaces: {
packages: ['packages/*'],
},
}),
'/packages/a/package.json': JSON.stringify({
name: 'a',
dependencies: {
'@backstage/core': '^1.0.5',
},
}),
'/packages/b/package.json': JSON.stringify({
name: 'b',
dependencies: {
'@backstage/core': '^1.0.3',
'@backstage/theme': '^1.0.0',
},
}),
});
jest
.spyOn(paths, 'resolveTargetRoot')
.mockImplementation((...path) => resolvePath('/', ...path));
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
worker.use(
rest.get(
'https://versions.backstage.io/v1/tags/main/manifest.json',
(_, res, ctx) =>
res(
ctx.status(200),
ctx.json({
packages: [],
}),
),
),
);
const { log: logs } = await withLogCollector(['log'], async () => {
await bump({ pattern: null, release: 'main' } as unknown as Command);
});
expect(logs.filter(Boolean)).toEqual([
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core-api',
'Some packages are outdated, updating',
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
'bumping @backstage/core in a to ^1.0.6',
'bumping @backstage/core in b to ^1.0.6',
'bumping @backstage/theme in b to ^2.0.0',
'Running yarn install to install new versions',
'⚠️ The following packages may have breaking changes:',
' @backstage/theme : 1.0.0 ~> 2.0.0',
' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md',
'Version bump complete!',
'The following packages have duplicates but have been allowed:',
'first-duplicate, second-duplicate, third-duplicate',
]);
});
});
describe('bumpBackstageJsonVersion', () => {
+13 -4
View File
@@ -314,10 +314,6 @@ export default async (opts: OptionValues) => {
),
});
if (result.newVersions.length > 0) {
throw new Error('Duplicate versions present after package bump');
}
const forbiddenNewRanges = result.newRanges.filter(({ name }) =>
forbiddenDuplicatesFilter(name),
);
@@ -328,6 +324,19 @@ export default async (opts: OptionValues) => {
.join(', ')}`,
);
}
const allowedDuplicates = result.newRanges.filter(
({ name }) => !forbiddenDuplicatesFilter(name),
);
if (allowedDuplicates.length > 0) {
console.log(
chalk.yellow(
'The following packages have duplicates but have been allowed:',
),
);
console.log(chalk.yellow(allowedDuplicates.map(i => i.name).join(', ')));
}
};
export function createStrictVersionFinder(options: {
+10
View File
@@ -1,5 +1,15 @@
# @backstage/config-loader
## 1.5.0-next.3
### Patch Changes
- Updated dependencies
- @backstage/config@1.1.0-next.2
- @backstage/errors@1.2.2-next.0
- @backstage/types@1.1.1-next.0
- @backstage/cli-common@0.1.12
## 1.5.0-next.2
### Patch Changes

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