Merge remote-tracking branch 'upstream/master' into master
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-lighthouse': patch
|
||||
---
|
||||
|
||||
Strip trailing slash from url when creating a new audit. This change prevents duplicate audits from being displayed in the audit list.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
---
|
||||
|
||||
Support HTTP 400 Bad Request from Kubernetes API
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-import': patch
|
||||
---
|
||||
|
||||
Improve how URLs are analyzed for add/import
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
'@backstage/techdocs-common': patch
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
---
|
||||
|
||||
Improve techdocs-common Generator API for it to be used by techdocs-cli. TechDocs generator.run function now takes
|
||||
an input AND an output directory. Most probably you use techdocs-common via plugin-techdocs-backend, and so there
|
||||
is no breaking change for you.
|
||||
But if you use techdocs-common separately, you need to create an output directory and pass into the generator.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
---
|
||||
|
||||
Revamped Kubernetes UI and added error reporting/detection
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/integration': minor
|
||||
---
|
||||
|
||||
Build out the `ScmIntegrations` class, as well as the individual `*Integration` classes
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
Export the `schemaValidator` helper function.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-jenkins': patch
|
||||
---
|
||||
|
||||
Handle missing ObjectMetadataAction in Jenkins API
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-import': patch
|
||||
---
|
||||
|
||||
Add more generic descriptions for the catalog-import form.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/techdocs-common': patch
|
||||
---
|
||||
|
||||
Fix bug where binary files (`png`, etc.) could not load when using AWS or GCS publisher.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
'@backstage/techdocs-common': patch
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Google Cloud authentication in TechDocs has been improved.
|
||||
|
||||
1. `techdocs.publisher.googleGcs.credentials` is now optional. If it is missing, `GOOGLE_APPLICATION_CREDENTIALS`
|
||||
environment variable (and some other methods) will be used to authenticate.
|
||||
Read more here https://cloud.google.com/docs/authentication/production
|
||||
|
||||
2. `techdocs.publisher.googleGcs.projectId` is no longer used. You can remove it from your `app-config.yaml`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Append `-credentials.yaml` to credentials file generated by `backstage-cli create-github-app` and display warning about sensitive contents.
|
||||
@@ -2,4 +2,4 @@
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
AWS SDK version bump for Catalog Backend.
|
||||
Refuse to remove the bootstrap location
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-import': patch
|
||||
---
|
||||
|
||||
Remove dependency to `@backstage/plugin-catalog-backend`.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Enable catalog table actions for all location types.
|
||||
|
||||
The edit button has had support for other providers for a while and there is
|
||||
no specific reason the View in GitHub cannot work for all locations. This
|
||||
change also replaces the GitHub icon with the OpenInNew icon.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/core': patch
|
||||
---
|
||||
|
||||
Fix issue where `SidebarItem` with `onClick` and without `to` renders an inaccessible div. It now renders a button.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/techdocs-common': patch
|
||||
---
|
||||
|
||||
AWS SDK version bump for TechDocs.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
---
|
||||
|
||||
Allow blank certificates and support logout URLs in the SAML provider.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Added experimental `create-github-app` command.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Add `*-credentials.yaml` to gitignore to prevent accidental commits of sensitive credential information.
|
||||
|
||||
To apply this change to an existing installation, add these lines to your `.gitignore`
|
||||
|
||||
```gitignore
|
||||
# Sensitive credentials
|
||||
*-credentials.yaml
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Reduce log noise on locations refresh
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
'@backstage/techdocs-common': patch
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
AWS S3 authentication in TechDocs has been improved.
|
||||
|
||||
1. `techdocs.publisher.awsS3.bucketName` is now the only required config. `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are optional.
|
||||
|
||||
2. If `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are missing, the AWS environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be used. There are more better ways of setting up AWS authentication. Read the guide at https://backstage.io/docs/features/techdocs/using-cloud-storage
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/techdocs-common': patch
|
||||
---
|
||||
|
||||
@backstage/techdocs-common can now be imported in an environment without @backstage/plugin-techdocs-backend being installed.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Add subcomponentOf to Component kind to represent subsystems of larger components.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Remove the `@types/helmet` dev dependency from the app template. This
|
||||
dependency is now unused as the package `helmet` brings its own types.
|
||||
|
||||
To update your existing app, simply remove the `@types/helmet` dependency from
|
||||
the `package.json` of your backend package.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Update the @azure/msal-node dependency to 1.0.0-beta.3.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Export all preparers and publishers properly
|
||||
@@ -13,3 +13,4 @@
|
||||
/plugins/techdocs-backend @backstage/techdocs-core
|
||||
/packages/techdocs-common @backstage/techdocs-core
|
||||
/.changeset/cost-insights-* @backstage/silver-lining
|
||||
/.changeset/techdocs-* @backstage/techdocs-core
|
||||
|
||||
@@ -70,6 +70,7 @@ Ek
|
||||
env
|
||||
Env
|
||||
eslint
|
||||
Expedia
|
||||
facto
|
||||
failover
|
||||
Figma
|
||||
@@ -77,6 +78,7 @@ Firekube
|
||||
Fiverr
|
||||
freben
|
||||
Fredrik
|
||||
gitbeaker
|
||||
GitHub
|
||||
GitLab
|
||||
Grafana
|
||||
@@ -107,6 +109,7 @@ Kaewkasi
|
||||
Knex
|
||||
kubectl
|
||||
kubernetes
|
||||
Kumar
|
||||
learnings
|
||||
lerna
|
||||
Lerna
|
||||
@@ -142,6 +145,7 @@ npm
|
||||
nvarchar
|
||||
nvm
|
||||
OAuth
|
||||
octokit
|
||||
oidc
|
||||
Okta
|
||||
Oldsberg
|
||||
@@ -181,6 +185,8 @@ rollbar
|
||||
Rollbar
|
||||
Rollup
|
||||
Rosaceae
|
||||
routable
|
||||
Routable
|
||||
rst
|
||||
rsync
|
||||
rugvip
|
||||
@@ -193,6 +199,7 @@ semlas
|
||||
semver
|
||||
Serverless
|
||||
Sinon
|
||||
Sneha
|
||||
Snyk
|
||||
sourcemaps
|
||||
sparklines
|
||||
|
||||
@@ -27,6 +27,9 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: verify doc links
|
||||
run: node scripts/verify-links.js
|
||||
|
||||
# Skip caching of microsite dependencies, it keeps the global cache size
|
||||
# smaller, which make Windows builds a lot faster for the rest of the project.
|
||||
- name: yarn install
|
||||
|
||||
@@ -130,3 +130,6 @@ site
|
||||
|
||||
# Local configuration files
|
||||
*.local.yaml
|
||||
|
||||
# Sensitive credentials
|
||||
*-credentials.yaml
|
||||
|
||||
+20
-19
@@ -1,19 +1,20 @@
|
||||
| Organization | Contact | Description of Use |
|
||||
| -------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. |
|
||||
| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. |
|
||||
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
|
||||
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
|
||||
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
|
||||
| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. |
|
||||
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
|
||||
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
|
||||
| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. |
|
||||
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
|
||||
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D |
|
||||
| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling |
|
||||
| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling |
|
||||
| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks |
|
||||
| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. |
|
||||
| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. |
|
||||
| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. |
|
||||
| Organization | Contact | Description of Use |
|
||||
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. |
|
||||
| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. |
|
||||
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
|
||||
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
|
||||
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
|
||||
| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. |
|
||||
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
|
||||
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
|
||||
| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. |
|
||||
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
|
||||
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D |
|
||||
| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling |
|
||||
| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling |
|
||||
| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks |
|
||||
| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. |
|
||||
| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. |
|
||||
| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. |
|
||||
| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo) | EG Common Developer Toolkit |
|
||||
|
||||
+21
-3
@@ -127,7 +127,16 @@ integrations:
|
||||
|
||||
catalog:
|
||||
rules:
|
||||
- allow: [Component, API, Group, User, Template, Location]
|
||||
- allow:
|
||||
- Component
|
||||
- API
|
||||
- Resource
|
||||
- Group
|
||||
- User
|
||||
- Template
|
||||
- System
|
||||
- Domain
|
||||
- Location
|
||||
|
||||
processors:
|
||||
githubOrg:
|
||||
@@ -178,12 +187,21 @@ catalog:
|
||||
# Example component for github-actions
|
||||
- type: url
|
||||
target: https://github.com/backstage/backstage/blob/master/plugins/github-actions/examples/sample.yaml
|
||||
# Example component for techdocs
|
||||
# Example component for TechDocs
|
||||
- type: url
|
||||
target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml
|
||||
target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml
|
||||
# Backstage example APIs
|
||||
- type: url
|
||||
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml
|
||||
# Backstage example resources
|
||||
- type: url
|
||||
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-resources.yaml
|
||||
# Backstage example systems
|
||||
- type: url
|
||||
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-systems.yaml
|
||||
# Backstage example domains
|
||||
- type: url
|
||||
target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-domains.yaml
|
||||
# Backstage example templates
|
||||
- type: url
|
||||
target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ metadata:
|
||||
Backstage is an open-source developer portal that puts the developer experience first.
|
||||
annotations:
|
||||
github.com/project-slug: backstage/backstage
|
||||
backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git
|
||||
backstage.io/techdocs-ref: url:https://github.com/backstage/backstage/tree/master
|
||||
lighthouse.com/website-url: https://backstage.io
|
||||
spec:
|
||||
type: library
|
||||
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
HeaderLabel,
|
||||
SupportButton,
|
||||
identityApiRef,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import { useApi } from '@backstage/core-api';
|
||||
import ExampleFetchComponent from '../ExampleFetchComponent';
|
||||
|
||||
const ExampleComponent = () => {
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
TableColumn,
|
||||
Progress,
|
||||
githubAuthApiRef,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import { useApi } from '@backstage/core-api';
|
||||
import { graphql } from '@octokit/graphql';
|
||||
|
||||
const query = `{
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
id: how-to-guides
|
||||
title: TechDocs "HOW TO" guides
|
||||
sidebar_label: "HOW TO" guides
|
||||
description: TechDocs "HOW TO" guides related to TechDocs
|
||||
---
|
||||
|
||||
## How to use URL Reader in TechDocs Prepare step?
|
||||
|
||||
If TechDocs is configured to generate docs, it will first download the
|
||||
repository associated with the `backstage.io/techdocs-ref` annotation defined in
|
||||
the Entity's `catalog-info.yaml` file. This is also called the
|
||||
[Prepare](./concepts.md#techdocs-preparer) step.
|
||||
|
||||
There are two kinds of preparers or two ways of downloading these source files
|
||||
|
||||
- Preparer 1: Doing a `git clone` of the repository (also known as Common Git
|
||||
Preparer)
|
||||
- Preparer 2: Downloading an archive.zip or equivalent of the repository (also
|
||||
known as URL Reader)
|
||||
|
||||
If `backstage.io/techdocs-ref` is equal to any of these -
|
||||
|
||||
1. `github:https://githubhost.com/org/repo`
|
||||
2. `gitlab:https://gitlabhost.com/org/repo`
|
||||
3. `bitbucket:https://bitbuckethost.com/project/repo`
|
||||
4. `azure/api:https://azurehost.com/org/project`
|
||||
|
||||
Then Common Git Preparer will be used i.e. a `git clone`. But the URL Reader is
|
||||
a much faster way to do this step. Convert the `backstage.io/techdocs-ref`
|
||||
values to the following -
|
||||
|
||||
1. `url:https://githubhost.com/org/repo/tree/<branch_name>`
|
||||
2. `url:https://gitlabhost.com/org/repo/tree/<branch_name>`
|
||||
3. `url:https://bitbuckethost.com/project/repo/src/<branch_name>`
|
||||
4. `url:https://azurehost.com/organization/project/_git/repository`
|
||||
|
||||
Note that you can also provide a path to a non-root directory inside the
|
||||
repository which contains the `docs/` directory.
|
||||
|
||||
e.g.
|
||||
`url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component`
|
||||
|
||||
### Why is URL Reader faster than a git clone?
|
||||
|
||||
URL Reader uses the source code hosting provider to download a zip or tarball of
|
||||
the repository. The archive does not have any git history attached to it. Also
|
||||
it is a compressed file. Hence the file size is significantly smaller than how
|
||||
much data git clone has to transfer.
|
||||
|
||||
Caveat: Currently TechDocs sites built using URL Reader will be cached for 30
|
||||
minutes which means they will not be re-built if new changes are made within 30
|
||||
minutes. This cache invalidation will be replaced by commit timestamp based
|
||||
implementation very soon.
|
||||
@@ -5,6 +5,53 @@ sidebar_label: Troubleshooting
|
||||
description: Troubleshooting for TechDocs
|
||||
---
|
||||
|
||||
- TechDocs will fail to clone your docs if you have a git config which overrides
|
||||
the `https` protocol with `ssh` or something else. Make sure to remove your
|
||||
git config locally when you try TechDocs.
|
||||
## Failure to clone
|
||||
|
||||
TechDocs will fail to clone your docs if you have a git config which overrides
|
||||
the `https` protocol with `ssh` or something else. Make sure to remove your git
|
||||
config locally when you try TechDocs.
|
||||
|
||||
## MkDocs Build Errors
|
||||
|
||||
Using the [TechDocs CLI](https://github.com/backstage/techdocs-cli), you can
|
||||
troubleshoot MkDocs build issues locally. Note this requires you have Docker
|
||||
available to launch images. First, `git clone` the target repository locally,
|
||||
then in the root of the repository, run:
|
||||
|
||||
```
|
||||
npx @techdocs/cli serve
|
||||
```
|
||||
|
||||
For example, if you have forgotten to put an MkDocs configuration file in your
|
||||
repo, the resulting error will be:
|
||||
|
||||
```
|
||||
npx: installed 278 in 9.089s
|
||||
[techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000
|
||||
INFO - Building documentation...
|
||||
|
||||
Config file '/content/mkdocs.yml' does not exist.
|
||||
```
|
||||
|
||||
When it works, a local copy of both Backstage and your site will be launched
|
||||
locally:
|
||||
|
||||
```
|
||||
npx: installed 278 in 9.682s
|
||||
[techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000
|
||||
INFO - Building documentation...
|
||||
WARNING - Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0'
|
||||
suggests a production environment or the use of a proxy to connect to the MkDocs
|
||||
server. However, the MkDocs' server is intended for local development purposes only.
|
||||
Please use a third party production-ready server instead.
|
||||
INFO - Cleaning site directory
|
||||
DEBUG - Successfully imported extension module "plantuml_markdown".
|
||||
DEBUG - Successfully loaded extension "plantuml_markdown.PlantUMLMarkdownExtension".
|
||||
INFO - Documentation built in 0.23 seconds
|
||||
[I 210115 19:00:45 server:335] Serving on http://0.0.0.0:8000
|
||||
INFO - Serving on http://0.0.0.0:8000
|
||||
[I 210115 19:00:45 handlers:62] Start watching changes
|
||||
INFO - Start watching changes
|
||||
[I 210115 19:00:45 handlers:64] Start detecting changes
|
||||
INFO - Start detecting changes
|
||||
```
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
id: Glossary
|
||||
title: Backstage Glossary
|
||||
# prettier-ignore
|
||||
description: List of all the terms, abbreviations, and phrases used in Backstage, together with their explanations.
|
||||
---
|
||||
|
||||
The Backstage Glossary lists all the terms, abbreviations, and phrases used in
|
||||
Backstage, together with their explanations. We encourage you to use the
|
||||
terminology below for clarity and consistency when discussing Backstage.
|
||||
|
||||
### Backstage User Profiles
|
||||
|
||||
There are three main user profiles for Backstage: the integrator, the
|
||||
contributor, and the software engineer.
|
||||
|
||||
| Term | Explanation |
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Integrator | The **integrator** hosts the Backstage app and configures which plugins are available to use in the app. |
|
||||
| Contributor | The **contributor** adds functionality to the app by writing plugins. |
|
||||
| Software Engineer | The **software engineer** uses the app's functionality and interacts with its plugins. In practice, this profile covers the various roles that help deliver software, from the Software Engineer themselves, to Designers, Data Scientists, Product Owners, Engineering Managers, etc. |
|
||||
@@ -0,0 +1,585 @@
|
||||
---
|
||||
id: composability
|
||||
title: Composability System Migration
|
||||
# prettier-ignore
|
||||
description: Documentation and migration instructions for new composability APIs.
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This page describes the new composability system that was recently introduced in
|
||||
Backstage, and it does so from the perspective of the existing patterns and
|
||||
APIs. As the new system is solidified and existing code is ported, this page
|
||||
will be removed and replaced with a more direct description of the composability
|
||||
system. For now, the primary purpose of this documentation is to aid in the
|
||||
migration of existing plugins, but it does cover the migration of apps as well.
|
||||
|
||||
The core principle of the new composability system is that plugins should have
|
||||
clear boundaries and connections. It should isolate crashes within a plugin, but
|
||||
allow navigation between them. It should allow for plugins to be loaded only
|
||||
when needed, and enable plugins to provide extension points for other plugins to
|
||||
build upon. The composability system is also built with an app-first mindset,
|
||||
prioritizing simplicity and clarity in the app over that in the plugins and core
|
||||
APIs.
|
||||
|
||||
The new composability system isn't a single new API surface. It is a collection
|
||||
of patterns, primitives, new APIs, and old APIs used in new ways. At the core is
|
||||
the new concept of extensions, which are exported by plugins for use in the app.
|
||||
There is also a new primitive called component data, which assists in the
|
||||
conversion to a more declarative app. The `RouteRef`s now have a clear purpose
|
||||
as well, and can be used route to pages in a flexible way.
|
||||
|
||||
## New Concepts
|
||||
|
||||
This section is a brief look into all the new and updated concepts that were put
|
||||
in place to support the new composability system.
|
||||
|
||||
### Component Data
|
||||
|
||||
Component data is a new composability primitive that is introduced as a way to
|
||||
provide a new data dimension for React components. Data is attached to React
|
||||
components using a key, and is then readable from any JSX elements created with
|
||||
those components, using the same key, as illustrated by the following example:
|
||||
|
||||
```tsx
|
||||
const MyComponent = () => <h1>This is my component</h1>;
|
||||
attachComponentData(MyComponent, 'my.data', 5);
|
||||
|
||||
const element = <MyComponent />;
|
||||
const myData = getComponentData(element, 'my.data');
|
||||
// myData === 5
|
||||
```
|
||||
|
||||
The purpose of component data is to provide a method for embedding data that can
|
||||
be inspected before rendering elements. Element inspection is a pattern that is
|
||||
quite common among React libraries, and used for example by `react-router` and
|
||||
`material-ui` to discover properties of the child elements before rendering.
|
||||
Although in those libraries only the element type and props are typically
|
||||
inspected, while our component data adds more structured access and simplifies
|
||||
evolution by allowing for multiple different versions of a piece of data to be
|
||||
used and interpreted at once.
|
||||
|
||||
The initial use-case for component data is to support route and plugin discovery
|
||||
through elements in the app. Through this we allow for the React element tree in
|
||||
the app to be the source of truth, both for which plugins are used, as well as
|
||||
all top-level plugin routes in the app. The use of component data is not limited
|
||||
to these use-cases though, as it can be used as a primitive to create new
|
||||
abstractions as well.
|
||||
|
||||
### Extensions
|
||||
|
||||
Extensions are what plugins export for use in an app. Most typically they are
|
||||
React components, but in practice they can be any kind of JavaScript value. They
|
||||
are created using `create*Extension` functions, and wrapped with
|
||||
`plugin.provide()` in order to create the actual exported extension.
|
||||
|
||||
The extension type is a simple one:
|
||||
|
||||
```ts
|
||||
export type Extension<T> = {
|
||||
expose(plugin: BackstagePlugin<any, any>): T;
|
||||
};
|
||||
```
|
||||
|
||||
The power of extensions comes from the ability of various actors to hook into
|
||||
their usage. The creation and plugin wrapping is controlled by whoever owns the
|
||||
creation function, the Backstage core is able to hook into the process of
|
||||
exposing the extension outside the plugin, and in the end the app controls the
|
||||
usage of the extension.
|
||||
|
||||
The Backstage core API currently provides two different types of extension
|
||||
creators, `createComponentExtension`, and `createRoutableExtension`. Component
|
||||
extensions are plain React component with no particular requirements, for
|
||||
example a card for an entity overview page. The component will be exported more
|
||||
or less as is, but is wrapped to provide things like an error boundary, lazy
|
||||
loading, and a plugin context.
|
||||
|
||||
Routable extensions build on top of component extensions and are used for any
|
||||
component that should be rendered at a specific route path, such as top-level
|
||||
pages or entity page tab content. When creating a routable extension you need to
|
||||
supply a `RouteRef` as `mountPoint`. The mount point will be the handle of the
|
||||
component for the outside world, and is used by other components and plugins
|
||||
that wish to link to the routable component.
|
||||
|
||||
As of now there are only two extension creation functions, but it is possible to
|
||||
add more of them in the future, both in the core library and in plugins that
|
||||
wish to provide an extension point for other plugins to build upon. Extensions
|
||||
are also not tied to React, and can both be used to model generic JavaScript
|
||||
concepts, as well as potentially bridge to rendering libraries and web
|
||||
frameworks other than React.
|
||||
|
||||
### Extensions from a Plugin's Point of View
|
||||
|
||||
Extensions are one of the primary methods to traverse the plugin boundary, and
|
||||
the way that plugins provide concrete content for use within an app. They
|
||||
replace existing component export concepts such as `Router` or `*Card`s for
|
||||
display on entity overview pages.
|
||||
|
||||
It is recommended to create the exported extensions either in the top-level
|
||||
`plugin.ts` file, or in a dedicated `extensions.ts` (or `.tsx`) file. That file
|
||||
should not contain the bulk of the implementation though, and in fact, if the
|
||||
extension is a React component it is recommended to lazy-load the actual
|
||||
component. Component extensions support lazy loading out of the box using the
|
||||
`lazy` component declaration, for example:
|
||||
|
||||
```ts
|
||||
export const EntityFooCard = plugin.provide(
|
||||
createComponentExtension({
|
||||
component: {
|
||||
lazy: () => import('./components/FooCard').then(m => m.FooCard),
|
||||
},
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
Routable extensions even enforce lazy loading, as it is the only way to provide
|
||||
a component:
|
||||
|
||||
```ts
|
||||
export const FooPage = plugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () => import('./components/FooPage').then(m => m.FooPage),
|
||||
mountPoint: fooPageRouteRef,
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
### Using Extensions in an App
|
||||
|
||||
Right now all extensions are modelled as React components. The usage of these
|
||||
extension is like regular usage of any React components, with one important
|
||||
difference. Extensions must all be part of a single React element tree spanning
|
||||
from the root `AppProvider`.
|
||||
|
||||
For example, the following app code does **NOT** work:
|
||||
|
||||
```tsx
|
||||
const AppRoutes = () => (
|
||||
<Routes>
|
||||
<Route path="/foo" element={<FooPage />} />
|
||||
<Route path="/bar" element={<BarPage />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
const App = () => (
|
||||
<AppProvider>
|
||||
<AppRouter>
|
||||
<Root>
|
||||
<AppRoutes />
|
||||
</Root>
|
||||
</AppRouter>
|
||||
</AppProvider>
|
||||
);
|
||||
```
|
||||
|
||||
But in this case it is simple to fix! Simply be sure to not create any
|
||||
intermediate components in the app, for example like this:
|
||||
|
||||
```tsx
|
||||
const appRoutes = (
|
||||
<Routes>
|
||||
<Route path="/foo" element={<FooPage />} />
|
||||
<Route path="/bar" element={<BarPage />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
const App = () => (
|
||||
<AppProvider>
|
||||
<AppRouter>
|
||||
<Root>{appRoutes}</Root>
|
||||
</AppRouter>
|
||||
</AppProvider>
|
||||
);
|
||||
```
|
||||
|
||||
### New Routing System
|
||||
|
||||
A big piece of what is enabled by moving over to this new composability system
|
||||
is to make `RouteRef`s useful. The `RouteRef`s no longer have their own path, in
|
||||
fact the only required parameter is currently a `title`. Instead of assigning a
|
||||
path to each `RouteRef` and possibly overriding these paths in the app, the
|
||||
concrete `path` for each `RouteRef` is discovered based on the element tree in
|
||||
the app. Let's consider the following example:
|
||||
|
||||
```tsx
|
||||
const appRoutes = (
|
||||
<Routes>
|
||||
<Route path="/foo" element={<FooPage />} />
|
||||
<Route path="/bar" element={<BarPage />} />
|
||||
</Routes>
|
||||
);
|
||||
```
|
||||
|
||||
We'll assume that `FooPage` and `BarPage` are routable extensions, exported by
|
||||
the `fooPlugin` and `barPlugin` respectively. Since the `FooPage` is a routable
|
||||
extension it has a `RouteRef` assigned as its mount point, which we'll refer to
|
||||
as `fooPageRouteRef`.
|
||||
|
||||
Given the above example, the `fooPageRouteRef` will be associated with the
|
||||
`'/foo'` route. The path is no longer accessible via the `path` property of the
|
||||
`RouteRef` though, as the routing structure is tied to the app's react tree. We
|
||||
instead use the new `useRouteRef` hook if we want to create a concrete link to
|
||||
the page. The `useRouteRef` hook takes a single `RouteRef` as its only
|
||||
parameter, and returns a function that is called to create the URL. For example
|
||||
like this:
|
||||
|
||||
```tsx
|
||||
const MyComponent = () => {
|
||||
const fooRoute = useRouteRef(fooPageRouteRef);
|
||||
return <a href={fooRoute()}>Link to Foo</a>;
|
||||
};
|
||||
```
|
||||
|
||||
Now let's assume that we want to link from the `BarPage` to the `FooPage`.
|
||||
Before the introduction of the new composability system, we would do this by
|
||||
importing the `fooPageRouteRef` exported by the `fooPlugin`. This created an
|
||||
unnecessary dependency on the plugin, and also provided little flexibility in
|
||||
allowing the app to tie plugins together, with the links instead being dictated
|
||||
by the plugins themselves. To solve this, we introduce `ExternalRouteRef`s. Much
|
||||
like regular route references, they can be passed to `useRouteRef` to create
|
||||
concrete URLs, but they can not be used as mount points in routable component
|
||||
and instead have to be associated with a target route using route bindings in
|
||||
the app.
|
||||
|
||||
We create a new `ExternalRouteRef` inside the `barPlugin`, using a neutral name
|
||||
that describes its role in the plugin rather than a specific plugin page that it
|
||||
might be linking to, allowing the app to decide the final target. If the
|
||||
`BarPage` for example wants to link to an external page in the header, it might
|
||||
declare an `ExternalRouteRef` similar to this:
|
||||
|
||||
```ts
|
||||
const headerLinkRouteRef = createExternalRouteRef();
|
||||
```
|
||||
|
||||
### Binding External Routes in the App
|
||||
|
||||
The association of external routes is controlled by the app. Each
|
||||
`ExternalRouteRef` of a plugin should be bound to an actual `RouteRef`, usually
|
||||
from another plugin. The binding process happens once at app startup, and is
|
||||
then used through the lifetime of the app to help resolve concrete route paths.
|
||||
|
||||
Using the above example of the `BarPage` linking to the `FooPage`, we might do
|
||||
something like this in the app:
|
||||
|
||||
```ts
|
||||
createApp({
|
||||
bindRoutes({ bind }) {
|
||||
bind(barPlugin.externalRoutes, {
|
||||
headerLink: fooPlugin.routes.root,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Given the above binding, using `useRouteRef(headerLinkRouteRef)` within the
|
||||
`barPlugin` will let us create a link to whatever path the `FooPage` is mounted
|
||||
at.
|
||||
|
||||
Note that we are not importing and using the `RouteRef`s directly in the app,
|
||||
and instead rely on the plugin instance to access routes of the plugins. This is
|
||||
a new convention that was introduced to provide better namespacing and
|
||||
discoverability of routes, as well as reduce the number of separate exports from
|
||||
each plugin package. The route references would be supplied to `createPlugin`
|
||||
like this:
|
||||
|
||||
```ts
|
||||
// In foo-plugin
|
||||
export const fooPlugin = createPlugin({
|
||||
routes: {
|
||||
root: fooPageRouteRef,
|
||||
},
|
||||
...
|
||||
})
|
||||
|
||||
// In bar-plugin
|
||||
export const barPlugin = createPlugin({
|
||||
externalRoutes: {
|
||||
headerLink: headerLinkRouteRef,
|
||||
},
|
||||
...
|
||||
})
|
||||
```
|
||||
|
||||
Also note that you almost always want to create the route references themselves
|
||||
in a different file than the one that creates the plugin instance, for example a
|
||||
top-level `routes.ts`. This is to avoid circular imports when you use the route
|
||||
references from other parts of the same plugin.
|
||||
|
||||
### Parameterized Routes
|
||||
|
||||
A new addition to `RouteRef`s is the possibility of adding named and typed
|
||||
parameters. Parameters are declared at creation, and will enforce presence of
|
||||
the parameters in the path in the app, and require them as a parameter when
|
||||
using `useRouteRef`.
|
||||
|
||||
The following is an example of creation and usage of a parameterized route:
|
||||
|
||||
```tsx
|
||||
// Creation of a parameterized route
|
||||
const myRouteRef = createRouteRef({
|
||||
title: 'My Named Route',
|
||||
params: ['name']
|
||||
})
|
||||
|
||||
// In the app, where MyPage is a routable extension with myRouteRef set as mountPoint
|
||||
<Route path='/my-page/:name' element={<MyPage />}/>
|
||||
|
||||
// Usage within a component
|
||||
const myRoute = useRouteRef(myRouteRef)
|
||||
return (
|
||||
<div>
|
||||
<a href={myRoute({name: 'a'})}>A</a>
|
||||
<a href={myRoute({name: 'b'})}>B</a>
|
||||
</div>
|
||||
)
|
||||
```
|
||||
|
||||
It is currently not possible to have parameterized `ExternalRouteRef`s, or to
|
||||
bind an external route to a parameterized route, although this may be added in
|
||||
the future if needed.
|
||||
|
||||
### New Catalog Components
|
||||
|
||||
The established pattern for selecting what plugins should be available on each
|
||||
catalog page is to use custom components in the app, with logic embedded in the
|
||||
render function. Typically this takes form as a component that either receives
|
||||
the entity via props or uses the `useEntity` hook to retrieve the selected
|
||||
entity. A `switch` or `if` / `else if` chain is then used to select what
|
||||
children should be rendered based on information in the entity.
|
||||
|
||||
This pattern will no longer work with the new composability system, and in
|
||||
general is very difficult to build any form of declarative model around, as it
|
||||
depends on runtime execution. To help replace existing code, a new
|
||||
`EntitySwitch` component has been added to the `@backstage/catalog` plugin,
|
||||
which grabs the selected entity from a context, and selects at most one element
|
||||
to render using a list of `EntitySwitch.Case` children.
|
||||
|
||||
For example, if you want all entities of kind `"Template"` to be rendered with a
|
||||
`MyTemplate` component, and all other entities to be rendered with a `MyOther`
|
||||
component, you would do the following:
|
||||
|
||||
```tsx
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={isKind('template')}>
|
||||
<MyTemplate />
|
||||
</EntitySwitch.Case>
|
||||
|
||||
<EntitySwitch.Case>
|
||||
<MyTemplate />
|
||||
</EntitySwitch.Case>
|
||||
</EntitySwitch>
|
||||
|
||||
// Shorter form if desired:
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={isKind('template')} children={<MyTemplate />}/>
|
||||
<EntitySwitch.Case children={<MyTemplate />}/>
|
||||
</EntitySwitch>
|
||||
```
|
||||
|
||||
The `EntitySwitch` component will render the children of the first
|
||||
`EntitySwitch.Case` that returns `true` when the selected entity is passed to
|
||||
the function of the `if` prop. If none of the cases match, no children will be
|
||||
rendered, and if a case doesn't specify an `if` filter function, it will always
|
||||
match. The `if` property is simply a function of the type
|
||||
`(entity: Entity) => boolean`, for example, `isKind` can be implemented like
|
||||
this:
|
||||
|
||||
```ts
|
||||
function isKind(kind: string) {
|
||||
return (entity: Entity) => entity.kind.toLowerCase() === kind.toLowerCase();
|
||||
}
|
||||
```
|
||||
|
||||
The `@backstage/catalog` plugin provides a couple of built-in conditions,
|
||||
`isKind`, `isComponentType`, and `isNamespace`.
|
||||
|
||||
In addition to the `EntitySwitch` component, the catalog plugin also exports a
|
||||
new `EntityLayout` component. It is a tweaked version and replacement for the
|
||||
`EntityPageLayout` component, and is introduced more in depth in the app
|
||||
migration section below.
|
||||
|
||||
## Porting Existing Plugins
|
||||
|
||||
There are a couple of high-level steps to porting an existing plugin to the new
|
||||
composability system:
|
||||
|
||||
- Remove usage of `router.addRoute` or `router.registerRoute` within
|
||||
`createPlugin`, and export the page components as routable extensions instead.
|
||||
- Switch any `Router` export to instead be a routable extension.
|
||||
- Change any plain component exports, such as catalog overview cards, to be
|
||||
component extensions.
|
||||
- Stop exporting `RouteRef`s and instead pass them to `createPlugin`.
|
||||
- Stop accepting `RouteRef`s as props or importing them from other plugins,
|
||||
instead create an `ExternalRouteRef` as a replacement, and pass it to
|
||||
`createPlugin.`
|
||||
- Rename any other exported symbols according to the naming pattern table below.
|
||||
|
||||
Note that removing the existing exports and configuration is a breaking change
|
||||
in any plugin. If backwards compatibility is needed the existing code be
|
||||
deprecated while making the new additions, to then be removed at a later point.
|
||||
|
||||
### Naming Patterns
|
||||
|
||||
Many export naming patterns have been changed to avoid import aliases and to
|
||||
clarify intent. Refer to the following table to formulate the new name:
|
||||
|
||||
| Description | Existing Pattern | New Pattern | Examples |
|
||||
| -------------------- | -------------------------- | --------------- | ---------------------------------------------- |
|
||||
| Top-level Pages | Router | \*Page | CatalogIndexPage, SettingsPage, LighthousePage |
|
||||
| Entity Tab Content | Router | Entity\*Content | EntityJenkinsContent, EntityKubernetesContent |
|
||||
| Entity Overview Card | \*Card | Entity\*Card | EntitySentryCard, EntityPagerDutyCard |
|
||||
| Entity Conditional | isPluginApplicableToEntity | is\*Available | isPagerDutyAvailable, isJenkinsAvailable |
|
||||
| Plugin Instance | plugin | \*Plugin | jenkinsPlugin, catalogPlugin |
|
||||
|
||||
## Porting Existing Apps
|
||||
|
||||
The first step of porting any app is to replace the root `Routes` component with
|
||||
`FlatRoutes` from `@backstage/core`. As opposed to the `Routes` component,
|
||||
`FlatRoutes` only considers the first level of `Route` components in its
|
||||
children, and provides any additional children to the outlet of the route. It
|
||||
also removes the need to append `"/*"` to paths, as it is added automatically.
|
||||
|
||||
```diff
|
||||
const AppRoutes = () => (
|
||||
- <Routes>
|
||||
+ <FlatRoutes>
|
||||
...
|
||||
- <Route path="/docs/*" element={<DocsRouter />} />
|
||||
+ <Route path="/docs" element={<DocsRouter />} />
|
||||
...
|
||||
- </Routes>
|
||||
+ </FlatRoutes>
|
||||
);
|
||||
```
|
||||
|
||||
The next step should be to switch from using `EntityPageLayout` to
|
||||
`EntityLayout`, as this can also be done without waiting for plugins to be
|
||||
ported. You should also replace the top-level `Router` from the catalog plugin
|
||||
with the separate `CatalogIndexPage` and `CatalogEntityPage` extensions that
|
||||
have been added to the catalog:
|
||||
|
||||
```diff
|
||||
-<Route
|
||||
- path={`${catalogRouteRef.path}/*`}
|
||||
- element={<CatalogRouter EntityPage={EntityPage} />}
|
||||
-/>
|
||||
+<Route path="/catalog" element={<CatalogIndexPage />} />
|
||||
+<Route
|
||||
+ path="/catalog/:namespace/:kind/:name"
|
||||
+ element={<CatalogEntityPage />}
|
||||
+>
|
||||
+ <EntityPage />
|
||||
+</Route>
|
||||
```
|
||||
|
||||
At that point you should flatten out the element tree as much as possible in the
|
||||
app, removing any intermediate components. At the top level this should usually
|
||||
be straightforward, but when reaching the catalog entity pages you may need to
|
||||
wait for some plugins to be migrated. This is because it is no longer possible
|
||||
to pass in the selected entity through component props, and it should be picked
|
||||
up from context inside the plugin instead. See the sections below for how to
|
||||
carry out migrations of some common entity page patterns.
|
||||
|
||||
Once the app element tree doesn't contain any intermediate components, and all
|
||||
plugin imports have been switched to extensions rather than plain components,
|
||||
the app has been fully ported.
|
||||
|
||||
### Switching from EntityPageLayout to EntityLayout
|
||||
|
||||
The existing `EntityPageLayout` is replaced by the new `EntityLayout` component,
|
||||
which has a slightly different pattern for expressing the contents and paths.
|
||||
|
||||
Porting from the old to the new API is just a matter of moving some things
|
||||
around. For example, given the following existing code:
|
||||
|
||||
```tsx
|
||||
<EntityPageLayout>
|
||||
<EntityPageLayout.Content
|
||||
path="/"
|
||||
title="Overview"
|
||||
element={<ComponentOverviewContent entity={entity} />}
|
||||
/>
|
||||
<EntityPageLayout.Content
|
||||
path="/sentry"
|
||||
title="Sentry"
|
||||
element={<SentryRouter entity={entity} />}
|
||||
/>
|
||||
<EntityPageLayout.Content
|
||||
path="/kubernetes/*"
|
||||
title="Kubernetes"
|
||||
element={<KubernetesRouter entity={entity} />}
|
||||
/>
|
||||
</EntityPageLayout>
|
||||
```
|
||||
|
||||
It would be ported to this:
|
||||
|
||||
```tsx
|
||||
<EntityLayout>
|
||||
<EntityLayout.Route path="/" title="Overview">
|
||||
<ComponentOverviewContent entity={entity} />
|
||||
</EntityLayout.Route>
|
||||
|
||||
<EntityLayout.Route path="/sentry" title="Sentry">
|
||||
<SentryRouter entity={entity} />
|
||||
</EntityLayout.Route>
|
||||
|
||||
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
|
||||
<KubernetesRouter entity={entity} />
|
||||
</EntityLayout.Route>
|
||||
</EntityLayout>
|
||||
```
|
||||
|
||||
In addition to the renaming, the `element` prop has been moved to `children`.
|
||||
Also note that the `/*` suffix has been removed from the `"/kubernetes"` path,
|
||||
as it's now added automatically.
|
||||
|
||||
Usage of the `EntityLayout` component is required to be able to properly
|
||||
discover routes, and so it is required to apply this change before you can start
|
||||
using routable entity content extensions from plugins.
|
||||
|
||||
### Porting Entity Pages
|
||||
|
||||
The established pattern in the app is to use custom components in order to
|
||||
select what plugin components to render for a given entity. The new
|
||||
`EntitySwitch` component introduced above is what is intended to replace this
|
||||
pattern, now that the entire app needs to be rendered as a single element tree.
|
||||
For example, given the following existing code:
|
||||
|
||||
```tsx
|
||||
export const EntityPage = () => {
|
||||
const { entity } = useEntity();
|
||||
|
||||
switch (entity?.kind?.toLowerCase()) {
|
||||
case 'component':
|
||||
return <ComponentEntityPage entity={entity} />;
|
||||
case 'api':
|
||||
return <ApiEntityPage entity={entity} />;
|
||||
case 'group':
|
||||
return <GroupEntityPage entity={entity} />;
|
||||
case 'user':
|
||||
return <UserEntityPage entity={entity} />;
|
||||
default:
|
||||
return <DefaultEntityPage entity={entity} />;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
It would be migrated to this:
|
||||
|
||||
```tsx
|
||||
export const entityPage = (
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={isKind('component')} children={componentPage} />
|
||||
<EntitySwitch.Case if={isKind('api')} children={apiPage} />
|
||||
<EntitySwitch.Case if={isKind('group')} children={groupPage} />
|
||||
<EntitySwitch.Case if={isKind('user')} children={userPage} />
|
||||
<EntitySwitch.Case children={defaultPage} />
|
||||
</EntitySwitch>
|
||||
);
|
||||
```
|
||||
|
||||
Note that for example `<ComponentEntityPage ... />` has been changed to simply
|
||||
`componentPage`, that is because just like the `EntityPage` component, the
|
||||
`ComponentEntityPage` also needs to be ported to be an element rather a
|
||||
component in a similar way.
|
||||
@@ -3,20 +3,18 @@ id: quickstart-app-auth
|
||||
title: Monorepo App Setup With Authentication
|
||||
---
|
||||
|
||||
###### September 15th 2020 - @backstage/create-app - v0.1.1-alpha.21
|
||||
###### January 8th 2021 - @backstage/create-app - v0.4.5
|
||||
|
||||
<br />
|
||||
|
||||
> This document takes you through setting up a Backstage app that runs in your
|
||||
> own environment. It starts with a skeleton install and verifying of the
|
||||
> monorepo's functionality. Next, GitHub authentication is added and tested.
|
||||
> monorepo's functionality. Next, authentication is added and tested.
|
||||
>
|
||||
> This document assumes you have Node.js 12 active along with Yarn and Python.
|
||||
> Please note, that at the time of this writing, the current version is
|
||||
> 0.1.1-alpha.21. This guide can still be used with future versions, just,
|
||||
> verify as you go. If you run into issues, you can compare your setup with mine
|
||||
> here >
|
||||
> [simple-backstage-app](https://github.com/johnson-jesse/simple-backstage-app).
|
||||
> This document assumes you have Node.js 12 or 14 active along with Yarn and
|
||||
> Python. Please note, that at the time of this writing, the current version is
|
||||
> v0.4.5. This guide can still be used with future versions, just, verify as you
|
||||
> go.
|
||||
|
||||
# The Skeleton Application
|
||||
|
||||
@@ -55,7 +53,17 @@ guest. Let's fix that now and add auth.
|
||||
|
||||
# The Auth Configuration
|
||||
|
||||
1. Open `app-config.yaml` and change it as follows
|
||||
A default Backstage installation includes multiple authentication providers out
|
||||
of the box. The steps to enable new authentication providers in Backstage are
|
||||
very similar to each other, the biggest difference is usually configuring the
|
||||
external authentication provider. Please see a subset of possible providers and
|
||||
instructions to integrate them below. Steps 1 & 2 are described separately for
|
||||
each provider and steps beyond that are common for all.
|
||||
|
||||
<details><summary>GitHub</summary>
|
||||
<p>
|
||||
|
||||
### 1. Open `app-config.yaml` and change it as follows
|
||||
|
||||
_from:_
|
||||
|
||||
@@ -75,23 +83,229 @@ auth:
|
||||
$env: AUTH_GITHUB_CLIENT_ID
|
||||
clientSecret:
|
||||
$env: AUTH_GITHUB_CLIENT_SECRET
|
||||
## uncomment the following three lines if using enterprise
|
||||
## uncomment the following two lines if using enterprise
|
||||
# enterpriseInstanceUrl:
|
||||
# $env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
|
||||
```
|
||||
|
||||
2. Set environment variables in whatever fashion is easiest for you. I chose to
|
||||
add mine to my `.zshrc` profile.
|
||||
### 2. Generate a GitHub client ID and secret
|
||||
|
||||
- Log into http://github.com
|
||||
- Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth
|
||||
App)[https://github.com/settings/applications/new]
|
||||
- Set Homepage URL = `http://localhost:3000`
|
||||
- Set Callback URL = `http://localhost:7000/api/auth/github`
|
||||
- Click [Register application]
|
||||
- On the next page, copy and paste your new Client ID and Client Secret to
|
||||
environment variables defined in the `app-config.yaml` file,
|
||||
`AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details><summary>GitLab</summary>
|
||||
<p>
|
||||
|
||||
### 1. Open `app-config.yaml` and change it as follows
|
||||
|
||||
_from:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers: {}
|
||||
```
|
||||
|
||||
_to:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers:
|
||||
gitlab:
|
||||
development:
|
||||
clientId:
|
||||
$env: AUTH_GITLAB_CLIENT_ID
|
||||
clientSecret:
|
||||
$env: AUTH_GITLAB_CLIENT_SECRET
|
||||
audience: https://gitlab.com # Or your self-hosted GitLab instance URL
|
||||
```
|
||||
|
||||
### 2. Generate a GitLab Application client ID and secret
|
||||
|
||||
- Log into GitLab
|
||||
- Navigate to (Profile > Settings >
|
||||
Applications)[https://gitlab.com/-/profile/applications]
|
||||
- Name your application
|
||||
- Set Callback URL = `http://localhost:7000/api/auth/gitlab/handler/frame`
|
||||
- Select the following values:
|
||||
- `read_user` (Read the authenticated user's personal information)
|
||||
- `read_repository` (Allows read-only access to the repository)
|
||||
- `write_repository` (Allows read-write access to the repository)
|
||||
- `openid` (Authenticate using OpenID Connect)
|
||||
- `profile` (Allows read-only access to the user's personal information using
|
||||
OpenID Connect)
|
||||
- `email` (Allows read-only access to the user's primary email address using
|
||||
OpenID Connect)
|
||||
- Click [Save application]
|
||||
- On the next page, copy and paste your new Application ID and Secret to
|
||||
environment variables defined in the `app-config.yaml` file,
|
||||
`AUTH_GITLAB_CLIENT_ID` & `AUTH_GITLAB_CLIENT_SECRET`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details><summary>Google</summary>
|
||||
<p>
|
||||
|
||||
### 1. Open `app-config.yaml` and change it as follows
|
||||
|
||||
_from:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers: {}
|
||||
```
|
||||
|
||||
_to:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers:
|
||||
google:
|
||||
development:
|
||||
clientId:
|
||||
$env: AUTH_GOOGLE_CLIENT_ID
|
||||
clientSecret:
|
||||
$env: AUTH_GOOGLE_CLIENT_SECRET
|
||||
```
|
||||
|
||||
### 2. Generate Google Credentials in Google Cloud console
|
||||
|
||||
- Log into https://console.cloud.google.com
|
||||
- Select or create a new project from the dropdown on the top bar
|
||||
- Navigate to (APIs & Services >
|
||||
Credentials)[https://console.cloud.google.com/apis/credentials]
|
||||
- Click Create Credentials and select [OAuth client ID]
|
||||
- Select Web Application as the application type
|
||||
- Add new Authorised JavaScript origin = `http://localhost:3000`
|
||||
- Add new Authorised redirect URI =
|
||||
`http://localhost:7000/api/auth/google/handler/frame`
|
||||
- Click [Save application]
|
||||
- Google should display a modal with your Client ID and Secret. Copy and paste
|
||||
those to environment variables defined in the `app-config.yaml` file,
|
||||
`AUTH_GOOGLE_CLIENT_ID` & `AUTH_GOOGLE_CLIENT_SECRET`
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details><summary>Microsoft</summary>
|
||||
<p>
|
||||
|
||||
### 1. Open `app-config.yaml` and change it as follows
|
||||
|
||||
_from:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers: {}
|
||||
```
|
||||
|
||||
_to:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers:
|
||||
microsoft:
|
||||
development:
|
||||
clientId:
|
||||
$env: AUTH_MICROSOFT_CLIENT_ID
|
||||
clientSecret:
|
||||
$env: AUTH_MICROSOFT_CLIENT_SECRET
|
||||
tenantId:
|
||||
$env: AUTH_MICROSOFT_TENANT_ID
|
||||
```
|
||||
|
||||
### 2. Create a Microsoft App Registration in Microsoft Portal
|
||||
|
||||
- Log into https://portal.azure.com
|
||||
- Navigate to (Azure Active Directory > App
|
||||
Registrations)[https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps]
|
||||
- Create a New Registration
|
||||
- Add new Redirect URI = `http://localhost:3000`
|
||||
- Add new Authorised redirect URI =
|
||||
`http://localhost:7000/api/auth/microsoft/handler/frame`
|
||||
- Click [Save application]
|
||||
- Set environment variable `AUTH_MICROSOFT_CLIENT_ID` from
|
||||
`Application (client) Id` displayed on the directory page
|
||||
- Set environment variable `AUTH_MICROSOFT_TENANT_ID` from
|
||||
`Directory (tenant) ID` displayed on the directory page
|
||||
- Navigate to Certificates & Secrets section and click [Create a new secret]
|
||||
- Set environment variable `AUTH_MICROSOFT_CLIENT_SECRET` from the `value` field
|
||||
created.
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details><summary>Auth0</summary>
|
||||
<p>
|
||||
|
||||
### 1. Open `app-config.yaml` and change it as follows
|
||||
|
||||
_from:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers: {}
|
||||
```
|
||||
|
||||
_to:_
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
providers:
|
||||
auth0:
|
||||
development:
|
||||
clientId:
|
||||
$env: AUTH_AUTH0_CLIENT_ID
|
||||
clientSecret:
|
||||
$env: AUTH_AUTH0_CLIENT_SECRET
|
||||
domain:
|
||||
$env: AUTH_AUTH0_DOMAIN_ID
|
||||
```
|
||||
|
||||
### 2. Create an Auth0 application in the Auth0 management console
|
||||
|
||||
- Log into https://manage.auth0.com/dashboard/
|
||||
- Navigate to Applications
|
||||
- Create a New Application
|
||||
- Select Single Page Web Application
|
||||
- Go to Settings tab
|
||||
- Add new line to Allowed Callback URLs =
|
||||
`http://localhost:7000/api/auth/auth0/handler/frame`
|
||||
- Click [Save Changes]
|
||||
- Set environment variables displayed on the Basic Information page
|
||||
- `AUTH_AUTH0_CLIENT_ID` from `Client ID` displayed on Auth0 application page
|
||||
- `AUTH_AUTH0_CLIENT_SECRET` from `Client Secret` displayed on Auth0
|
||||
application page
|
||||
- `AUTH_AUTH0_DOMAIN_ID` from `Domain` displayed on Auth0 application page
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
### 3. Set environment variables in whatever fashion is easiest for you. I chose to
|
||||
|
||||
add mine to my `.zshrc` profile.
|
||||
|
||||
```zsh
|
||||
# For macOS Catalina & Z Shell
|
||||
# ------ simple-backstage-app GitHub
|
||||
#
|
||||
# (Change the name of the environment variables based on your auth setup above)
|
||||
export AUTH_GITHUB_CLIENT_ID=xxx
|
||||
export AUTH_GITHUB_CLIENT_SECRET=xxx
|
||||
# export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://github.{MY_BIZ}.com
|
||||
```
|
||||
|
||||
3. And of course I need to source that file.
|
||||
### 4. And of course I need to source that file.
|
||||
|
||||
```zsh
|
||||
# Loading the new variables
|
||||
@@ -107,26 +321,28 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
|
||||
> ...
|
||||
```
|
||||
|
||||
4. The values to replace `xxx` above come from your OAuth app setup.
|
||||
### 5. Open and change _root > packages > app > src >_ `App.tsx` to use correct
|
||||
|
||||
```
|
||||
> Log into http://github.com
|
||||
> Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth App)[https://github.com/settings/applications/new]
|
||||
> Set Homepage URL = http://localhost:3000
|
||||
> Set Callback URL = http://localhost:7000/api/auth/github
|
||||
> Click [Register application]
|
||||
> On the next page, copy and paste your new Client ID and Client Secret to the environment variables above, `AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET`
|
||||
> Don't forget to `source` that profile file again if necessary.
|
||||
```
|
||||
|
||||
5. Open and change _root > packages > app > src >_`App.tsx` as follows
|
||||
authentication provider reference
|
||||
|
||||
```tsx
|
||||
// Add the following imports to the existing list from core
|
||||
import { githubAuthApiRef, SignInPage } from '@backstage/core';
|
||||
```
|
||||
|
||||
6. In the same file, change the createApp function as follows
|
||||
Modify the imported reference based on the authentication method you selected
|
||||
above:
|
||||
|
||||
| Auth Provider | Import Name |
|
||||
| ------------- | ------------------- |
|
||||
| GitHub | githubAuthApiRef |
|
||||
| GitLab | gitlabAuthApiRef |
|
||||
| Google | googleAuthApiRef |
|
||||
| Microsoft | microsoftAuthApiRef |
|
||||
| Auth0 | auth0AuthApiRef |
|
||||
|
||||
### 6. In the same file, modify createApp
|
||||
|
||||
Remember to modify the provider information based on the table above.
|
||||
|
||||
```tsx
|
||||
const app = createApp({
|
||||
@@ -153,12 +369,18 @@ const app = createApp({
|
||||
});
|
||||
```
|
||||
|
||||
7. Start the backend and frontend as before
|
||||
After finishing setting up one (or multiple) authentication providers defined
|
||||
above you can start the backend and frontend as before
|
||||
|
||||
When the browser loads, you should be presented with a login page for GitHub.
|
||||
Login as usual with your GitHub account. If this is your first time, you will be
|
||||
asked to authorize and then are redirected to the catalog page if all is well.
|
||||
|
||||
For more information you can clone
|
||||
[the backstage-auth-example repository](https://github.com/RoadieHQ/backstage-auth-example).
|
||||
Each authentication setting is set up there on a branch named after the
|
||||
authentication provider.
|
||||
|
||||
# Where to go from here
|
||||
|
||||
> You're probably eager to write your first custom plugin. Follow this next
|
||||
|
||||
@@ -72,8 +72,7 @@ Our first modification will be to extract information from the Identity API.
|
||||
|
||||
```tsx
|
||||
// Add identityApiRef to the list of imported from core
|
||||
import { identityApiRef } from '@backstage/core';
|
||||
import { useApi } from '@backstage/core-api';
|
||||
import { identityApiRef, useApi } from '@backstage/core';
|
||||
```
|
||||
|
||||
3. Adjust the ExampleComponent from inline to block
|
||||
@@ -143,8 +142,8 @@ import {
|
||||
TableColumn,
|
||||
Progress,
|
||||
githubAuthApiRef,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import { useApi } from '@backstage/core-api';
|
||||
import { graphql } from '@octokit/graphql';
|
||||
|
||||
const ExampleFetchComponent = () => {
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"features/techdocs/creating-and-publishing",
|
||||
"features/techdocs/configuration",
|
||||
"features/techdocs/using-cloud-storage",
|
||||
"features/techdocs/how-to-guides",
|
||||
"features/techdocs/troubleshooting",
|
||||
"features/techdocs/faqs"
|
||||
]
|
||||
@@ -95,6 +96,7 @@
|
||||
"plugins/plugin-development",
|
||||
"plugins/structure-of-a-plugin",
|
||||
"plugins/integrating-plugin-into-service-catalog",
|
||||
"plugins/composability",
|
||||
{
|
||||
"type": "subcategory",
|
||||
"label": "Backends and APIs",
|
||||
@@ -176,6 +178,7 @@
|
||||
],
|
||||
"Contribute": ["../CONTRIBUTING"],
|
||||
"Support": ["support/support", "support/project-structure"],
|
||||
"Glossary": ["glossary"],
|
||||
"FAQ": ["FAQ"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,11 @@ h6 {
|
||||
color: $textColor;
|
||||
}
|
||||
|
||||
summary {
|
||||
color: $textColor;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h2:hover .hash-link {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ nav:
|
||||
- Creating and Publishing Documentation: 'features/techdocs/creating-and-publishing.md'
|
||||
- Configuration: 'features/techdocs/configuration.md'
|
||||
- Using Cloud Storage: 'features/techdocs/using-cloud-storage.md'
|
||||
- HOW TO guides: 'features/techdocs/how-to-guides.md'
|
||||
- Troubleshooting: 'features/techdocs/troubleshooting.md'
|
||||
- FAQ: 'features/techdocs/FAQ.md'
|
||||
- Kubernetes:
|
||||
@@ -66,6 +67,7 @@ nav:
|
||||
- Creating a new plugin: 'plugins/create-a-plugin.md'
|
||||
- Developing a plugin: 'plugins/plugin-development.md'
|
||||
- Structure of a plugin: 'plugins/structure-of-a-plugin.md'
|
||||
- Composability System Migration: 'plugins/composability.md'
|
||||
- Backends and APIs:
|
||||
- Proxying: 'plugins/proxying.md'
|
||||
- Backstage backend plugin: 'plugins/backend-plugin.md'
|
||||
@@ -117,4 +119,5 @@ nav:
|
||||
- Support:
|
||||
- 'support/support.md'
|
||||
- 'support/project-structure.md'
|
||||
- Glossary: glossary.md
|
||||
- FAQ: FAQ.md
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.11.0",
|
||||
"@octokit/openapi-types": "^2.0.0",
|
||||
"@octokit/openapi-types": "^2.2.0",
|
||||
"@spotify/eslint-config-oss": "^1.0.1",
|
||||
"@spotify/prettier-config": "^9.0.0",
|
||||
"command-exists": "^1.2.9",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"@octokit/rest": "^18.0.12",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^0.1.3",
|
||||
"@roadiehq/backstage-plugin-github-insights": "^0.2.16",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^0.6.3",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @backstage/backend-common
|
||||
|
||||
## 0.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [466354aaa]
|
||||
- @backstage/integration@0.2.0
|
||||
|
||||
## 0.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/backend-common",
|
||||
"description": "Common functionality library for Backstage backends",
|
||||
"version": "0.4.2",
|
||||
"version": "0.4.3",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -32,7 +32,7 @@
|
||||
"@backstage/cli-common": "^0.1.1",
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/config-loader": "^0.4.1",
|
||||
"@backstage/integration": "^0.1.5",
|
||||
"@backstage/integration": "^0.2.0",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.6",
|
||||
"archiver": "^5.0.2",
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.5",
|
||||
"@backstage/cli": "^0.4.6",
|
||||
"@backstage/test-utils": "^0.1.5",
|
||||
"@types/archiver": "^5.1.0",
|
||||
"@types/compression": "^1.7.0",
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# example-backend
|
||||
|
||||
## 0.2.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cc068c0d6: Bump the gitbeaker dependencies to 28.x.
|
||||
|
||||
To update your own installation, go through the `package.json` files of all of
|
||||
your packages, and ensure that all dependencies on `@gitbeaker/node` or
|
||||
`@gitbeaker/core` are at version `^28.0.2`. Then run `yarn install` at the root
|
||||
of your repo.
|
||||
|
||||
- Updated dependencies [68ad5af51]
|
||||
- Updated dependencies [5a9a7e7c2]
|
||||
- Updated dependencies [f3b064e1c]
|
||||
- Updated dependencies [94fdf4955]
|
||||
- Updated dependencies [cc068c0d6]
|
||||
- Updated dependencies [ade6b3bdf]
|
||||
- Updated dependencies [468579734]
|
||||
- Updated dependencies [cb7af51e7]
|
||||
- Updated dependencies [abbee6fff]
|
||||
- Updated dependencies [147fadcb9]
|
||||
- Updated dependencies [711ba55a2]
|
||||
- @backstage/plugin-techdocs-backend@0.5.3
|
||||
- @backstage/plugin-kubernetes-backend@0.2.4
|
||||
- @backstage/catalog-model@0.6.1
|
||||
- @backstage/plugin-catalog-backend@0.5.3
|
||||
- @backstage/plugin-scaffolder-backend@0.4.1
|
||||
- @backstage/plugin-auth-backend@0.2.10
|
||||
- @backstage/backend-common@0.4.3
|
||||
|
||||
## 0.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.2.10",
|
||||
"version": "0.2.11",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -27,21 +27,21 @@
|
||||
"migrate:create": "knex migrate:make -x ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.4.1",
|
||||
"@backstage/catalog-model": "^0.6.0",
|
||||
"@backstage/backend-common": "^0.4.3",
|
||||
"@backstage/catalog-model": "^0.6.1",
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/plugin-app-backend": "^0.3.3",
|
||||
"@backstage/plugin-auth-backend": "^0.2.7",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.1",
|
||||
"@backstage/plugin-auth-backend": "^0.2.10",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.3",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.4",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.2.3",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.2.4",
|
||||
"@backstage/plugin-kafka-backend": "^0.1.0",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.3",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.5",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.4.0",
|
||||
"@backstage/plugin-techdocs-backend": "^0.5.0",
|
||||
"@gitbeaker/node": "^25.2.0",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.4.1",
|
||||
"@backstage/plugin-techdocs-backend": "^0.5.3",
|
||||
"@gitbeaker/node": "^28.0.2",
|
||||
"@octokit/rest": "^18.0.12",
|
||||
"azure-devops-node-api": "^10.1.1",
|
||||
"dockerode": "^3.2.1",
|
||||
"example-app": "^0.2.8",
|
||||
@@ -54,11 +54,10 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.3",
|
||||
"@backstage/cli": "^0.4.6",
|
||||
"@types/dockerode": "^3.2.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
"@types/helmet": "^0.0.48"
|
||||
"@types/express-serve-static-core": "^4.17.5"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @backstage/catalog-model
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f3b064e1c: Export the `schemaValidator` helper function.
|
||||
- abbee6fff: Implement System, Domain and Resource entity kinds.
|
||||
- 147fadcb9: Add subcomponentOf to Component kind to represent subsystems of larger components.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: example-domains
|
||||
description: A collection of all Backstage example domains
|
||||
spec:
|
||||
targets:
|
||||
- ./domains/artists-domain.yaml
|
||||
- ./domains/playback-domain.yaml
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: example-resources
|
||||
description: A collection of all Backstage example resources
|
||||
spec:
|
||||
targets:
|
||||
- ./resources/artists-db-resource.yaml
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: example-systems
|
||||
description: A collection of all Backstage example systems
|
||||
spec:
|
||||
targets:
|
||||
- ./systems/artist-engagement-portal-system.yaml
|
||||
- ./systems/audio-playback-system.yaml
|
||||
- ./systems/podcast-system.yaml
|
||||
@@ -10,3 +10,4 @@ spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: team-a
|
||||
system: artist-engagement-portal
|
||||
|
||||
@@ -7,3 +7,4 @@ spec:
|
||||
type: library
|
||||
lifecycle: experimental
|
||||
owner: team-c
|
||||
system: audio-playback
|
||||
|
||||
@@ -10,3 +10,4 @@ spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: user:guest
|
||||
system: audio-playback
|
||||
|
||||
@@ -9,3 +9,4 @@ spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: team-b
|
||||
system: podcast
|
||||
|
||||
@@ -10,3 +10,4 @@ spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: team-b
|
||||
system: podcast
|
||||
|
||||
@@ -9,3 +9,4 @@ spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: user:guest
|
||||
system: audio-playback
|
||||
|
||||
@@ -7,3 +7,4 @@ spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: team-a
|
||||
system: artist-engagement-portal
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Domain
|
||||
metadata:
|
||||
name: artists
|
||||
description: Everything related to artists
|
||||
spec:
|
||||
owner: team-a
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Domain
|
||||
metadata:
|
||||
name: playback
|
||||
description: Everything related to audio playback
|
||||
spec:
|
||||
owner: user:frank.tiernan
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Resource
|
||||
metadata:
|
||||
name: artists-db
|
||||
description: Stores artist details
|
||||
spec:
|
||||
type: database
|
||||
owner: team-a
|
||||
system: artist-engagement-portal
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: System
|
||||
metadata:
|
||||
name: artist-engagement-portal
|
||||
description: Everything related to artists
|
||||
tags:
|
||||
- portal
|
||||
spec:
|
||||
owner: team-a
|
||||
domain: artists
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: System
|
||||
metadata:
|
||||
name: audio-playback
|
||||
description: Audio playback system
|
||||
spec:
|
||||
owner: team-c
|
||||
domain: playback
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: System
|
||||
metadata:
|
||||
name: podcast
|
||||
description: Podcast playback
|
||||
spec:
|
||||
owner: team-b
|
||||
domain: playback
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/catalog-model",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -38,7 +38,7 @@
|
||||
"yup": "^0.29.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.2",
|
||||
"@backstage/cli": "^0.4.6",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/lodash": "^4.14.151",
|
||||
|
||||
@@ -70,6 +70,7 @@ components:
|
||||
items:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
`,
|
||||
system: 'system',
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -152,4 +153,19 @@ components:
|
||||
(entity as any).spec.definition = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/definition/);
|
||||
});
|
||||
|
||||
it('accepts missing system', async () => {
|
||||
delete (entity as any).spec.system;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong system', async () => {
|
||||
(entity as any).spec.system = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
|
||||
it('rejects empty system', async () => {
|
||||
(entity as any).spec.system = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,6 +30,7 @@ const schema = yup.object<Partial<ApiEntityV1alpha1>>({
|
||||
lifecycle: yup.string().required().min(1),
|
||||
owner: yup.string().required().min(1),
|
||||
definition: yup.string().required().min(1),
|
||||
system: yup.string().notRequired().min(1),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
@@ -42,6 +43,7 @@ export interface ApiEntityV1alpha1 extends Entity {
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
definition: string;
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ describe('ComponentV1alpha1Validator', () => {
|
||||
subcomponentOf: 'monolith',
|
||||
providesApis: ['api-0'],
|
||||
consumesApis: ['api-0'],
|
||||
system: 'system',
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -158,4 +159,19 @@ describe('ComponentV1alpha1Validator', () => {
|
||||
(entity as any).spec.consumesApis = [];
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('accepts missing system', async () => {
|
||||
delete (entity as any).spec.system;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong system', async () => {
|
||||
(entity as any).spec.system = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
|
||||
it('rejects empty system', async () => {
|
||||
(entity as any).spec.system = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ const schema = yup.object<Partial<ComponentEntityV1alpha1>>({
|
||||
subcomponentOf: yup.string().notRequired().min(1),
|
||||
providesApis: yup.array(yup.string().required()).notRequired(),
|
||||
consumesApis: yup.array(yup.string().required()).notRequired(),
|
||||
system: yup.string().notRequired().min(1),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
@@ -46,6 +47,7 @@ export interface ComponentEntityV1alpha1 extends Entity {
|
||||
subcomponentOf?: string;
|
||||
providesApis?: string[];
|
||||
consumesApis?: string[];
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 {
|
||||
DomainEntityV1alpha1,
|
||||
domainEntityV1alpha1Validator as validator,
|
||||
} from './DomainEntityV1alpha1';
|
||||
|
||||
describe('DomainV1alpha1Validator', () => {
|
||||
let entity: DomainEntityV1alpha1;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Domain',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
},
|
||||
spec: {
|
||||
owner: 'me',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('happy path: accepts valid data', async () => {
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('silently accepts v1beta1 as well', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta1';
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('ignores unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('ignores unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('rejects missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects wrong owner', async () => {
|
||||
(entity as any).spec.owner = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects empty owner', async () => {
|
||||
(entity as any).spec.owner = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 * as yup from 'yup';
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import { schemaValidator } from './util';
|
||||
|
||||
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
|
||||
const KIND = 'Domain' as const;
|
||||
|
||||
const schema = yup.object<Partial<DomainEntityV1alpha1>>({
|
||||
apiVersion: yup.string().required().oneOf(API_VERSION),
|
||||
kind: yup.string().required().equals([KIND]),
|
||||
spec: yup
|
||||
.object({
|
||||
owner: yup.string().required().min(1),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
|
||||
export interface DomainEntityV1alpha1 extends Entity {
|
||||
apiVersion: typeof API_VERSION[number];
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
owner: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const domainEntityV1alpha1Validator = schemaValidator(
|
||||
KIND,
|
||||
API_VERSION,
|
||||
schema,
|
||||
);
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 {
|
||||
ResourceEntityV1alpha1,
|
||||
resourceEntityV1alpha1Validator as validator,
|
||||
} from './ResourceEntityV1alpha1';
|
||||
|
||||
describe('ResourceV1alpha1Validator', () => {
|
||||
let entity: ResourceEntityV1alpha1;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Resource',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
},
|
||||
spec: {
|
||||
type: 'database',
|
||||
owner: 'me',
|
||||
system: 'system',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('happy path: accepts valid data', async () => {
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('silently accepts v1beta1 as well', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta1';
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('ignores unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('ignores unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('rejects missing type', async () => {
|
||||
delete (entity as any).spec.type;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects wrong type', async () => {
|
||||
(entity as any).spec.type = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects empty type', async () => {
|
||||
(entity as any).spec.type = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects wrong owner', async () => {
|
||||
(entity as any).spec.owner = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects empty owner', async () => {
|
||||
(entity as any).spec.owner = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('accepts missing system', async () => {
|
||||
delete (entity as any).spec.system;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong system', async () => {
|
||||
(entity as any).spec.system = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
|
||||
it('rejects empty system', async () => {
|
||||
(entity as any).spec.system = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 * as yup from 'yup';
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import { schemaValidator } from './util';
|
||||
|
||||
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
|
||||
const KIND = 'Resource' as const;
|
||||
|
||||
const schema = yup.object<Partial<ResourceEntityV1alpha1>>({
|
||||
apiVersion: yup.string().required().oneOf(API_VERSION),
|
||||
kind: yup.string().required().equals([KIND]),
|
||||
spec: yup
|
||||
.object({
|
||||
type: yup.string().required().min(1),
|
||||
owner: yup.string().required().min(1),
|
||||
system: yup.string().notRequired().min(1),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
|
||||
export interface ResourceEntityV1alpha1 extends Entity {
|
||||
apiVersion: typeof API_VERSION[number];
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
type: string;
|
||||
owner: string;
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const resourceEntityV1alpha1Validator = schemaValidator(
|
||||
KIND,
|
||||
API_VERSION,
|
||||
schema,
|
||||
);
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 {
|
||||
SystemEntityV1alpha1,
|
||||
systemEntityV1alpha1Validator as validator,
|
||||
} from './SystemEntityV1alpha1';
|
||||
|
||||
describe('SystemV1alpha1Validator', () => {
|
||||
let entity: SystemEntityV1alpha1;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'System',
|
||||
metadata: {
|
||||
name: 'test',
|
||||
},
|
||||
spec: {
|
||||
owner: 'me',
|
||||
domain: 'domain',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('happy path: accepts valid data', async () => {
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('silently accepts v1beta1 as well', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta1';
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('ignores unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('ignores unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('rejects missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects wrong owner', async () => {
|
||||
(entity as any).spec.owner = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects empty owner', async () => {
|
||||
(entity as any).spec.owner = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('accepts missing domain', async () => {
|
||||
delete (entity as any).spec.domain;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong domain', async () => {
|
||||
(entity as any).spec.domain = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/domain/);
|
||||
});
|
||||
|
||||
it('rejects empty domain', async () => {
|
||||
(entity as any).spec.domain = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/domain/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 * as yup from 'yup';
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import { schemaValidator } from './util';
|
||||
|
||||
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
|
||||
const KIND = 'System' as const;
|
||||
|
||||
const schema = yup.object<Partial<SystemEntityV1alpha1>>({
|
||||
apiVersion: yup.string().required().oneOf(API_VERSION),
|
||||
kind: yup.string().required().equals([KIND]),
|
||||
spec: yup
|
||||
.object({
|
||||
owner: yup.string().required().min(1),
|
||||
domain: yup.string().notRequired().min(1),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
|
||||
export interface SystemEntityV1alpha1 extends Entity {
|
||||
apiVersion: typeof API_VERSION[number];
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
owner: string;
|
||||
domain?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const systemEntityV1alpha1Validator = schemaValidator(
|
||||
KIND,
|
||||
API_VERSION,
|
||||
schema,
|
||||
);
|
||||
@@ -26,6 +26,11 @@ export type {
|
||||
ComponentEntityV1alpha1 as ComponentEntity,
|
||||
ComponentEntityV1alpha1,
|
||||
} from './ComponentEntityV1alpha1';
|
||||
export { domainEntityV1alpha1Validator } from './DomainEntityV1alpha1';
|
||||
export type {
|
||||
DomainEntityV1alpha1 as DomainEntity,
|
||||
DomainEntityV1alpha1,
|
||||
} from './DomainEntityV1alpha1';
|
||||
export { groupEntityV1alpha1Validator } from './GroupEntityV1alpha1';
|
||||
export type {
|
||||
GroupEntityV1alpha1 as GroupEntity,
|
||||
@@ -37,6 +42,16 @@ export type {
|
||||
LocationEntityV1alpha1,
|
||||
} from './LocationEntityV1alpha1';
|
||||
export * from './relations';
|
||||
export { resourceEntityV1alpha1Validator } from './ResourceEntityV1alpha1';
|
||||
export type {
|
||||
ResourceEntityV1alpha1 as ResourceEntity,
|
||||
ResourceEntityV1alpha1,
|
||||
} from './ResourceEntityV1alpha1';
|
||||
export { systemEntityV1alpha1Validator } from './SystemEntityV1alpha1';
|
||||
export type {
|
||||
SystemEntityV1alpha1 as SystemEntity,
|
||||
SystemEntityV1alpha1,
|
||||
} from './SystemEntityV1alpha1';
|
||||
export { templateEntityV1alpha1Validator } from './TemplateEntityV1alpha1';
|
||||
export type {
|
||||
TemplateEntityV1alpha1 as TemplateEntity,
|
||||
|
||||
@@ -30,7 +30,7 @@ export const RELATION_OWNED_BY = 'ownedBy';
|
||||
export const RELATION_OWNER_OF = 'ownerOf';
|
||||
|
||||
/**
|
||||
* A relation with an API entity, typically from a component or system
|
||||
* A relation with an API entity, typically from a component
|
||||
*/
|
||||
export const RELATION_CONSUMES_API = 'consumesApi';
|
||||
export const RELATION_API_CONSUMED_BY = 'apiConsumedBy';
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @backstage/cli
|
||||
|
||||
## 0.4.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version
|
||||
- 08e9893d2: Handle no npm info
|
||||
- 9cf71f8bf: Added experimental `create-github-app` command.
|
||||
|
||||
## 0.4.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -34,7 +34,7 @@
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@lerna/package-graph": "^3.18.5",
|
||||
"@lerna/project": "^3.18.0",
|
||||
"@octokit/request": "^5.2.0",
|
||||
"@octokit/request": "^5.4.12",
|
||||
"@rollup/plugin-commonjs": "^16.0.0",
|
||||
"@rollup/plugin-json": "^4.0.2",
|
||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||
@@ -113,9 +113,9 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.4.2",
|
||||
"@backstage/backend-common": "^0.4.3",
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/core": "^0.4.3",
|
||||
"@backstage/core": "^0.4.4",
|
||||
"@backstage/dev-utils": "^0.1.7",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
|
||||
@@ -47,7 +47,6 @@ const FORM_PAGE = `
|
||||
|
||||
type GithubAppConfig = {
|
||||
appId: number;
|
||||
apiUrl: string;
|
||||
slug?: string;
|
||||
name?: string;
|
||||
webhookUrl?: string;
|
||||
@@ -88,14 +87,11 @@ export class GithubCreateAppServer {
|
||||
`POST /app-manifests/${encodeURIComponent(
|
||||
req.query.code as string,
|
||||
)}/conversions`,
|
||||
).then(({ data, url }) => {
|
||||
// url = https://api.github.com/app-manifests/<code>/conversions
|
||||
const apiUrl = url.replace(/(?:\/[^\/]+){3}$/, '');
|
||||
).then(({ data }) => {
|
||||
resolve({
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
appId: data.id,
|
||||
apiUrl,
|
||||
webhookUrl: this.webhookUrl,
|
||||
clientId: data.client_id,
|
||||
clientSecret: data.client_secret,
|
||||
|
||||
@@ -26,9 +26,14 @@ import { GithubCreateAppServer } from './GithubCreateAppServer';
|
||||
export default async (org: string) => {
|
||||
const { slug, name, ...config } = await GithubCreateAppServer.run({ org });
|
||||
|
||||
const fileName = `github-app-${slug}.yaml`;
|
||||
const fileName = `github-app-${slug}-credentials.yaml`;
|
||||
const content = `# Name: ${name}\n${stringifyYaml(config)}`;
|
||||
await fs.writeFile(paths.resolveTargetRoot(fileName), content);
|
||||
console.log(`GitHub App configuration written to ${chalk.cyan(fileName)}`);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'This file contains sensitive credentials, it should not be committed to version control and handled with care!',
|
||||
),
|
||||
);
|
||||
// TODO: log instructions on how to use the newly created app configuration.
|
||||
};
|
||||
|
||||
@@ -177,4 +177,71 @@ describe('bump', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore not found packages', async () => {
|
||||
// Make sure all modules involved in package discovery are in the module cache before we mock fs
|
||||
await mapDependencies(paths.targetDir);
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMockResult,
|
||||
'/lerna.json': JSON.stringify({
|
||||
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': '^2.0.0',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
paths.targetDir = '/';
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...paths) => resolvePath('/', ...paths));
|
||||
jest.spyOn(runObj, 'runPlain').mockImplementation(async () => '');
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
|
||||
const { log: logs } = await withLogCollector(['log'], async () => {
|
||||
await bump();
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
'Checking for updates of @backstage/theme',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Package info not found, ignoring package @backstage/theme',
|
||||
'Package info not found, ignoring package @backstage/core',
|
||||
'Checking for updates of @backstage/theme',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Package info not found, ignoring package @backstage/theme',
|
||||
'Package info not found, ignoring package @backstage/core',
|
||||
'All Backstage packages are up to date!',
|
||||
]);
|
||||
|
||||
expect(runObj.run).toHaveBeenCalledTimes(0);
|
||||
|
||||
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
|
||||
expect(lockfileContents).toBe(lockfileMockResult);
|
||||
|
||||
const packageA = await fs.readJson('/packages/a/package.json');
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5', // not bumped
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3', // not bumped
|
||||
'@backstage/theme': '^2.0.0', // not bumped
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +55,16 @@ export default async () => {
|
||||
// Track package versions that we want to remove from yarn.lock in order to trigger a bump
|
||||
const unlocked = Array<{ name: string; range: string; target: string }>();
|
||||
await workerThreads(16, dependencyMap.entries(), async ([name, pkgs]) => {
|
||||
const target = await findTargetVersion(name);
|
||||
let target: string;
|
||||
try {
|
||||
target = await findTargetVersion(name);
|
||||
} catch (error) {
|
||||
if (error.name === 'NotFoundError') {
|
||||
console.log(`Package info not found, ignoring package ${name}`);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (const pkg of pkgs) {
|
||||
if (semver.satisfies(target, pkg.range)) {
|
||||
@@ -84,7 +93,16 @@ export default async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = await findTargetVersion(name);
|
||||
let target: string;
|
||||
try {
|
||||
target = await findTargetVersion(name);
|
||||
} catch (error) {
|
||||
if (error.name === 'NotFoundError') {
|
||||
console.log(`Package info not found, ignoring package ${name}`);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (const entry of lockfile.get(name) ?? []) {
|
||||
// Ignore lockfile entries that don't satisfy the version range, since
|
||||
|
||||
@@ -44,3 +44,5 @@ export function exitWithError(error: Error): never {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends CustomError {}
|
||||
|
||||
@@ -19,6 +19,7 @@ import path from 'path';
|
||||
import * as runObj from '../run';
|
||||
import { paths } from '../paths';
|
||||
import { fetchPackageInfo, mapDependencies } from './packages';
|
||||
import { NotFoundError } from '../errors';
|
||||
|
||||
describe('fetchPackageInfo', () => {
|
||||
afterEach(() => {
|
||||
@@ -40,6 +41,14 @@ describe('fetchPackageInfo', () => {
|
||||
'my-package',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw if no info', async () => {
|
||||
jest.spyOn(runObj, 'runPlain').mockResolvedValue('');
|
||||
|
||||
await expect(fetchPackageInfo('my-package')).rejects.toThrow(
|
||||
new NotFoundError(`No package information found for package my-package`),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapDependencies', () => {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { runPlain } from '../../lib/run';
|
||||
import { NotFoundError } from '../errors';
|
||||
|
||||
const PREFIX = '@backstage';
|
||||
|
||||
@@ -49,6 +50,11 @@ export async function fetchPackageInfo(
|
||||
name: string,
|
||||
): Promise<YarnInfoInspectData> {
|
||||
const output = await runPlain('yarn', 'info', '--json', name);
|
||||
|
||||
if (!output) {
|
||||
throw new NotFoundError(`No package information found for package ${name}`);
|
||||
}
|
||||
|
||||
const info = JSON.parse(output) as YarnInfo;
|
||||
if (info.type !== 'inspect') {
|
||||
throw new Error(`Received unknown yarn info for ${name}, ${output}`);
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/core
|
||||
|
||||
## 0.4.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 265a7ab30: Fix issue where `SidebarItem` with `onClick` and without `to` renders an inaccessible div. It now renders a button.
|
||||
|
||||
## 0.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core",
|
||||
"description": "Core API used by Backstage plugins and apps",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.4",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -65,7 +65,7 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.4",
|
||||
"@backstage/cli": "^0.4.6",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @backstage/create-app
|
||||
|
||||
## 0.3.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version
|
||||
- cc068c0d6: Bump the gitbeaker dependencies to 28.x.
|
||||
|
||||
To update your own installation, go through the `package.json` files of all of
|
||||
your packages, and ensure that all dependencies on `@gitbeaker/node` or
|
||||
`@gitbeaker/core` are at version `^28.0.2`. Then run `yarn install` at the root
|
||||
of your repo.
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/create-app",
|
||||
"description": "Create app package for Backstage",
|
||||
"version": "0.3.4",
|
||||
"version": "0.3.5",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -37,29 +37,29 @@
|
||||
"recursive-readdir": "^2.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.4.2",
|
||||
"@backstage/catalog-model": "^0.6.0",
|
||||
"@backstage/cli": "^0.4.5",
|
||||
"@backstage/backend-common": "^0.4.3",
|
||||
"@backstage/catalog-model": "^0.6.1",
|
||||
"@backstage/cli": "^0.4.6",
|
||||
"@backstage/config": "^0.1.2",
|
||||
"@backstage/core": "^0.4.3",
|
||||
"@backstage/core": "^0.4.4",
|
||||
"@backstage/plugin-api-docs": "^0.4.2",
|
||||
"@backstage/plugin-app-backend": "^0.3.3",
|
||||
"@backstage/plugin-auth-backend": "^0.2.9",
|
||||
"@backstage/plugin-catalog": "^0.2.10",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.2",
|
||||
"@backstage/plugin-catalog-import": "^0.3.3",
|
||||
"@backstage/plugin-auth-backend": "^0.2.10",
|
||||
"@backstage/plugin-catalog": "^0.2.11",
|
||||
"@backstage/plugin-catalog-backend": "^0.5.3",
|
||||
"@backstage/plugin-catalog-import": "^0.3.4",
|
||||
"@backstage/plugin-circleci": "^0.2.5",
|
||||
"@backstage/plugin-explore": "^0.2.2",
|
||||
"@backstage/plugin-github-actions": "^0.2.6",
|
||||
"@backstage/plugin-lighthouse": "^0.2.6",
|
||||
"@backstage/plugin-github-actions": "^0.2.7",
|
||||
"@backstage/plugin-lighthouse": "^0.2.7",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.3",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.6",
|
||||
"@backstage/plugin-scaffolder": "^0.3.6",
|
||||
"@backstage/plugin-search": "^0.2.5",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.4.0",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.4.1",
|
||||
"@backstage/plugin-tech-radar": "^0.3.2",
|
||||
"@backstage/plugin-techdocs": "^0.5.2",
|
||||
"@backstage/plugin-techdocs-backend": "^0.5.2",
|
||||
"@backstage/plugin-techdocs": "^0.5.3",
|
||||
"@backstage/plugin-techdocs-backend": "^0.5.3",
|
||||
"@backstage/plugin-user-settings": "^0.2.3",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
|
||||
@@ -30,4 +30,7 @@ dist-types
|
||||
site
|
||||
|
||||
# Local configuration files
|
||||
*.local.yaml
|
||||
*.local.yaml
|
||||
|
||||
# Sensitive credentials
|
||||
*-credentials.yaml
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
"@backstage/plugin-proxy-backend": "^{{version '@backstage/plugin-proxy-backend'}}",
|
||||
"@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}",
|
||||
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"@gitbeaker/node": "^25.2.0",
|
||||
"@gitbeaker/node": "^28.0.2",
|
||||
"@octokit/rest": "^18.0.12",
|
||||
"dockerode": "^3.2.1",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
@@ -45,8 +45,7 @@
|
||||
"@backstage/cli": "^{{version '@backstage/cli'}}",
|
||||
"@types/dockerode": "^3.2.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
"@types/helmet": "^0.0.47"
|
||||
"@types/express-serve-static-core": "^4.17.5"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/integration
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 466354aaa: Build out the `ScmIntegrations` class, as well as the individual `*Integration` classes
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/integration",
|
||||
"version": "0.1.5",
|
||||
"version": "0.2.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -34,7 +34,7 @@
|
||||
"git-url-parse": "^11.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.4.5",
|
||||
"@backstage/cli": "^0.4.6",
|
||||
"@backstage/test-utils": "^0.1.5",
|
||||
"@types/jest": "^26.0.7",
|
||||
"msw": "^0.21.2"
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# @backstage/techdocs-common
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 68ad5af51: Improve techdocs-common Generator API for it to be used by techdocs-cli. TechDocs generator.run function now takes
|
||||
an input AND an output directory. Most probably you use techdocs-common via plugin-techdocs-backend, and so there
|
||||
is no breaking change for you.
|
||||
But if you use techdocs-common separately, you need to create an output directory and pass into the generator.
|
||||
- 371f67ecd: fix to-string breakage of binary files
|
||||
- f1e74777a: Fix bug where binary files (`png`, etc.) could not load when using AWS or GCS publisher.
|
||||
- dbe4450c3: Google Cloud authentication in TechDocs has been improved.
|
||||
|
||||
1. `techdocs.publisher.googleGcs.credentials` is now optional. If it is missing, `GOOGLE_APPLICATION_CREDENTIALS`
|
||||
environment variable (and some other methods) will be used to authenticate.
|
||||
Read more here https://cloud.google.com/docs/authentication/production
|
||||
|
||||
2. `techdocs.publisher.googleGcs.projectId` is no longer used. You can remove it from your `app-config.yaml`.
|
||||
|
||||
- 5826d0973: AWS SDK version bump for TechDocs.
|
||||
- b3b9445df: AWS S3 authentication in TechDocs has been improved.
|
||||
|
||||
1. `techdocs.publisher.awsS3.bucketName` is now the only required config. `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are optional.
|
||||
|
||||
2. If `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are missing, the AWS environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be used. There are more better ways of setting up AWS authentication. Read the guide at https://backstage.io/docs/features/techdocs/using-cloud-storage
|
||||
|
||||
- Updated dependencies [466354aaa]
|
||||
- Updated dependencies [f3b064e1c]
|
||||
- Updated dependencies [abbee6fff]
|
||||
- Updated dependencies [147fadcb9]
|
||||
- @backstage/integration@0.2.0
|
||||
- @backstage/catalog-model@0.6.1
|
||||
- @backstage/backend-common@0.4.3
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user