Merge branch 'master' of https://github.com/spotify/backstage
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: 'Feature Request'
|
||||
about: 'Suggest new features and changes'
|
||||
labels: help wanted
|
||||
labels: enhancement
|
||||
---
|
||||
|
||||
<!--- Provide a general summary of the feature request in the Title above -->
|
||||
|
||||
@@ -33,19 +33,11 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
- name: cache all node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }}
|
||||
- name: find location of global yarn cache
|
||||
id: yarn-cache
|
||||
if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: cache global yarn cache
|
||||
uses: actions/cache@v2
|
||||
if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: ${{ steps.yarn-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
@@ -25,19 +25,11 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
- name: cache all node_modules
|
||||
id: cache-modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }}
|
||||
- name: find location of global yarn cache
|
||||
id: yarn-cache
|
||||
if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: cache global yarn cache
|
||||
uses: actions/cache@v2
|
||||
if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: ${{ steps.yarn-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
@@ -74,3 +74,11 @@ jobs:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
package_root: "packages/core"
|
||||
tag_prefix: "v"
|
||||
|
||||
- name: Discord notification
|
||||
if: ${{ failure() }}
|
||||
uses: Ilshidur/action-discord@0.2.0
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
with:
|
||||
args: 'Master build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}'
|
||||
|
||||
@@ -8,6 +8,10 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re
|
||||
|
||||
> Collect changes for the next release below
|
||||
|
||||
## v0.1.1-alpha.20
|
||||
|
||||
- Includes https://github.com/spotify/backstage/pull/2097 to resolve issues with create-plugin command.
|
||||
|
||||
## v0.1.1-alpha.19
|
||||
|
||||
### @backstage/create-app
|
||||
|
||||
@@ -14,21 +14,15 @@ backend:
|
||||
client: sqlite3
|
||||
connection: ':memory:'
|
||||
|
||||
# See README.md in the proxy-backend plugin for information on the configuration format
|
||||
proxy:
|
||||
'/circleci/api':
|
||||
target: 'https://circleci.com/api/v1.1'
|
||||
changeOrigin: true
|
||||
pathRewrite:
|
||||
'^/proxy/circleci/api/': '/'
|
||||
'/circleci/api': https://circleci.com/api/v1.1
|
||||
'/jenkins/api':
|
||||
target: 'http://localhost:8080'
|
||||
changeOrigin: true
|
||||
target: http://localhost:8080
|
||||
headers:
|
||||
Authorization:
|
||||
$secret:
|
||||
env: JENKINS_BASIC_AUTH_HEADER
|
||||
pathRewrite:
|
||||
'^/proxy/jenkins/api/': '/'
|
||||
|
||||
organization:
|
||||
name: Spotify
|
||||
@@ -50,6 +44,46 @@ newrelic:
|
||||
baseUrl: 'https://api.newrelic.com/v2'
|
||||
key: NEW_RELIC_REST_API_KEY
|
||||
|
||||
lighthouse:
|
||||
baseUrl: http://localhost:3003
|
||||
|
||||
catalog:
|
||||
processors:
|
||||
githubApi:
|
||||
privateToken:
|
||||
$secret:
|
||||
env: GITHUB_PRIVATE_TOKEN
|
||||
bitbucketApi:
|
||||
username:
|
||||
$secret:
|
||||
env: BITBUCKET_USERNAME
|
||||
appPassword:
|
||||
$secret:
|
||||
env: BITBUCKET_APP_PASSWORD
|
||||
gitlabApi:
|
||||
privateToken:
|
||||
$secret:
|
||||
env: GITLAB_PRIVATE_TOKEN
|
||||
azureApi:
|
||||
privateToken:
|
||||
$secret:
|
||||
env: AZURE_PRIVATE_TOKEN
|
||||
exampleEntityLocations:
|
||||
github:
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-order-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/podcast-api-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/queue-proxy-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/searcher-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-lib-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/www-artist-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/shuffle-api-component.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml
|
||||
- https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml
|
||||
|
||||
auth:
|
||||
providers:
|
||||
google:
|
||||
@@ -124,8 +158,6 @@ auth:
|
||||
env: AUTH_AUTH0_DOMAIN
|
||||
microsoft:
|
||||
development:
|
||||
appOrigin: "http://localhost:3000/"
|
||||
secure: false
|
||||
clientId:
|
||||
$secret:
|
||||
env: AUTH_MICROSOFT_CLIENT_ID
|
||||
@@ -134,4 +166,4 @@ auth:
|
||||
env: AUTH_MICROSOFT_CLIENT_SECRET
|
||||
tenantId:
|
||||
$secret:
|
||||
env: AUTH_MICROSOFT_TENANT_ID
|
||||
env: AUTH_MICROSOFT_TENANT_ID
|
||||
|
||||
@@ -3,7 +3,7 @@ id: FAQ
|
||||
title: FAQ
|
||||
---
|
||||
|
||||
## Product FAQ:
|
||||
## Product FAQ
|
||||
|
||||
### Can we call Backstage something different? So that it fits our company better?
|
||||
|
||||
@@ -67,7 +67,7 @@ valuable as you grow.
|
||||
Yes! The Backstage UI is built using Material-UI. With the theming capabilities
|
||||
of Material-UI, you are able to adapt the interface to your brand guidelines.
|
||||
|
||||
## Technical FAQ:
|
||||
## Technical FAQ
|
||||
|
||||
### Why Material-UI?
|
||||
|
||||
|
||||
@@ -1,105 +1,3 @@
|
||||
# Documentation structure
|
||||
# Documentation
|
||||
|
||||
**Note!** This documentation structure is very much work in progress. If (when,
|
||||
really 😆) you find broken links or missing content, please create an issue or,
|
||||
better yet, a pull request.
|
||||
|
||||
# Plugins
|
||||
|
||||
- Overview
|
||||
- [What is Backstage?](overview/what-is-backstage.md)
|
||||
- [Backstage architecture](overview/architecture-overview.md)
|
||||
- [Architecture and terminology](overview/architecture-terminology.md)
|
||||
- [Roadmap](overview/roadmap.md)
|
||||
- [Vision](overview/vision.md)
|
||||
- [Strategies for adopting](overview/adopting.md)
|
||||
- Getting started
|
||||
- [Running Backstage locally](getting-started/index.md)
|
||||
- [Installation](getting-started/installation.md)
|
||||
- [Local development](getting-started/development-environment.md)
|
||||
- [Demo deployment](https://backstage-demo.roadie.io)
|
||||
- Production deployments
|
||||
- [Create an App](getting-started/create-an-app.md)
|
||||
- App configuration
|
||||
- [Configuring App with plugins](getting-started/configure-app-with-plugins.md)
|
||||
- [Customize the look-and-feel of your App](getting-started/app-custom-theme.md)
|
||||
- Deployment scenarios
|
||||
- [Kubernetes](getting-started/deployment-k8s.md)
|
||||
- [Other](getting-started/deployment-other.md)
|
||||
- Features
|
||||
- Software Catalog
|
||||
- [Overview](features/software-catalog/index.md)
|
||||
- [System model](features/software-catalog/system-model.md)
|
||||
- [YAML File Format](features/software-catalog/descriptor-format.md)
|
||||
- [Extending the model](features/software-catalog/extending-the-model.md)
|
||||
- [External integrations](features/software-catalog/external-integrations.md)
|
||||
- [API](features/software-catalog/api.md)
|
||||
- Software creation templates
|
||||
- [Overview](features/software-templates/index.md)
|
||||
- [Adding templates](features/software-templates/adding-templates.md)
|
||||
- Extending the Scaffolder:
|
||||
- [Overview](features/software-templates/extending/index.md)
|
||||
- [Create your own Templater](features/software-templates/extending/create-your-own-templater.md)
|
||||
- [Create your own Publisher](features/software-templates/extending/create-your-own-publisher.md)
|
||||
- [Create your own Preparer](features/software-templates/extending/create-your-own-preparer.md)
|
||||
- Docs-like-code
|
||||
- [Overview](features/techdocs/README.md)
|
||||
- [Getting Started](features/techdocs/getting-started.md)
|
||||
- [Concepts](features/techdocs/concepts.md)
|
||||
- [Creating and Publishing Documentation](features/techdocs/creating-and-publishing.md)
|
||||
- [FAQ](features/techdocs/FAQ.md)
|
||||
- Plugins
|
||||
- [Overview](plugins/index.md)
|
||||
- [Existing plugins](plugins/existing-plugins.md)
|
||||
- [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)
|
||||
- Backends and APIs
|
||||
- [Proxying](plugins/proxying.md)
|
||||
- [Backstage backend plugin](plugins/backend-plugin.md)
|
||||
- [Call existing API](plugins/call-existing-api.md)
|
||||
- Testing
|
||||
- [Overview](plugins/testing.md)
|
||||
- Publishing
|
||||
- [Open source and NPM](plugins/publishing.md)
|
||||
- [Private/internal (non-open source)](plugins/publish-private.md)
|
||||
- Configuration
|
||||
- [Overview](conf/index.md)
|
||||
- [Reading Configuration](conf/reading.md)
|
||||
- [Writing Configuration](conf/writing.md)
|
||||
- [Defining Configuration](conf/defining.md)
|
||||
- Authentication and identity
|
||||
- [Overview](auth/index.md)
|
||||
- [Add auth provider](auth/add-auth-provider.md)
|
||||
- [Auth backend](auth/auth-backend.md)
|
||||
- [OAuth](auth/oauth.md)
|
||||
- [Glossary](auth/glossary.md)
|
||||
- Designing for Backstage
|
||||
- [Backstage Design Language System (DLS)](dls/design.md)
|
||||
- [Storybook -- reusable UI components](http://backstage.io/storybook)
|
||||
- [Contributing to Storybook](dls/contributing-to-storybook.md)
|
||||
- [Figma resources](dls/figma.md)
|
||||
- API references
|
||||
- TypeScript API
|
||||
- [Utility APIs](api/utility-apis.md)
|
||||
- [Utility API References](reference/utility-apis/README.md)
|
||||
- [createPlugin](reference/createPlugin.md)
|
||||
- [createPlugin-feature-flags](reference/createPlugin-feature-flags.md)
|
||||
- [createPlugin-router](reference/createPlugin-router.md)
|
||||
- Backend APIs
|
||||
- [Backend](api/backend.md)
|
||||
- Tutorials
|
||||
- [Overview](tutorials/index.md)
|
||||
- Architecture Decision Records (ADRs)
|
||||
- [Overview](architecture-decisions/index.md)
|
||||
- [ADR001 - Architecture Decision Record (ADR) log](architecture-decisions/adr001-add-adr-log.md)
|
||||
- [ADR002 - Default Software Catalog File Format](architecture-decisions/adr002-default-catalog-file-format.md)
|
||||
- [ADR003 - Avoid Default Exports and Prefer Named Exports](architecture-decisions/adr003-avoid-default-exports.md)
|
||||
- [ADR004 - Module Export Structure](architecture-decisions/adr004-module-export-structure.md)
|
||||
- [ADR005 - Catalog Core Entities](architecture-decisions/adr005-catalog-core-entities.md)
|
||||
- [ADR006 - Avoid React.FC and React.SFC](architecture-decisions/adr006-avoid-react-fc.md)
|
||||
- [ADR007 - Use MSW for Mocking Network Requests](architecture-decisions/adr007-use-msw-to-mock-service-requests.md)
|
||||
- [ADR008 - Default Catalog File Name](architecture-decisions/adr008-default-catalog-file-name.md)
|
||||
- [Contribute](../CONTRIBUTING.md)
|
||||
- [Support](overview/support.md)
|
||||
- [FAQ](FAQ.md)
|
||||
The Backstage documentation is available at https://backstage.io/docs
|
||||
|
||||
@@ -4,8 +4,6 @@ title: Architecture Decision Records (ADR)
|
||||
sidebar_label: Overview
|
||||
---
|
||||
|
||||
#
|
||||
|
||||
The substantial architecture decisions made in the Backstage project lives here.
|
||||
For more information about ADRs, when to write them, and why, please see
|
||||
[this blog post](https://engineering.atspotify.com/2020/04/14/when-should-i-write-an-architecture-decision-record/).
|
||||
@@ -25,7 +23,10 @@ Records should be stored under the `architecture-decisions` directory.
|
||||
- Submit a pull request
|
||||
- Address and integrate feedback from the community
|
||||
- Eventually, assign a number
|
||||
- Add the full path of the ADR to the [`mkdocs.yml`](/mkdocs.yml)
|
||||
- Add the path of the ADR to the microsite sidebar in
|
||||
[`sidebars.json`](https://github.com/spotify/backstage/blob/master/microsite/sidebars.json)
|
||||
- Add the path of the ADR to the
|
||||
[`mkdocs.yml`](https://github.com/spotify/backstage/blob/master/mkdocs.yml)
|
||||
- Merge the pull request
|
||||
|
||||
## Superseding an ADR
|
||||
|
||||
|
Before Width: | Height: | Size: 303 KiB After Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 269 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 414 KiB After Width: | Height: | Size: 414 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 134 KiB |
@@ -1,4 +1,7 @@
|
||||
# Authentication Backend Classes Layout and Description
|
||||
---
|
||||
id: auth-backend-classes
|
||||
title: Auth backend classes
|
||||
---
|
||||
|
||||
## How Does Authentication Work?
|
||||
|
||||
|
||||
@@ -104,6 +104,8 @@ request an access token.
|
||||
|
||||
The following diagram visualizes the flow described in the previous section.
|
||||
|
||||

|
||||
|
||||
<!--
|
||||
@startuml oauth-popup-flow
|
||||
|
||||
@@ -152,5 +154,3 @@ Browser <- Backend: Tokens and info
|
||||
|
||||
@enduml
|
||||
-->
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: software-catalog-configuration
|
||||
title: Catalog Configuration
|
||||
---
|
||||
|
||||
## Static Location Configuration
|
||||
|
||||
To enable declarative catalog setups, it is possible to add locations to the
|
||||
catalog via [static configuration](../../conf/index.md). Locations are added to
|
||||
the catalog under the `catalog.locations` key, for example:
|
||||
|
||||
```yaml
|
||||
catalog:
|
||||
locations:
|
||||
- type: github
|
||||
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
|
||||
```
|
||||
|
||||
The locations added through static configuration can not be removed through the
|
||||
catalog locations API. To remove the locations, you have to remove them from the
|
||||
configuration.
|
||||
|
||||
## Catalog Rules
|
||||
|
||||
By default the catalog will only allow ingestion of entities with the kind
|
||||
`Component` and `API`. In order to allow entities of other kinds to be added,
|
||||
you need to add rules to the catalog. Rules are added either in a separate
|
||||
`catalog.rules` key, or added to statically configured locations.
|
||||
|
||||
For example, given the following configuration:
|
||||
|
||||
```yaml
|
||||
catalog:
|
||||
rules:
|
||||
- allow: [Component, API, System]
|
||||
|
||||
locations:
|
||||
- type: github
|
||||
target: https://github.com/org/example/blob/master/org-data.yaml
|
||||
allow: [Group]
|
||||
```
|
||||
|
||||
We are able to add entities of kind `Component`, `API`, or `System` from any
|
||||
location, and `Group` entities from the `org-data.yaml`, which will also be read
|
||||
as statically configured location.
|
||||
|
||||
Note that if the `catalog.rules` key is present it will replace the default
|
||||
value, meaning that you need to add rules for `Component` and `API` kinds if you
|
||||
want those to be allowed.
|
||||
|
||||
The following configuration will reject any kind of entities from being added to
|
||||
the catalog:
|
||||
|
||||
```yaml
|
||||
catalog:
|
||||
rules: []
|
||||
```
|
||||
@@ -12,7 +12,7 @@ Backstage natively supports tracking of the following component
|
||||
- Documentation
|
||||
- Other
|
||||
|
||||

|
||||

|
||||
|
||||
Since these types are likely not the only kind of software you will want to
|
||||
track in Backstage, it is possible to
|
||||
@@ -31,9 +31,10 @@ catalog.
|
||||
It might be tempting to put software that doesn't fit into any of the existing
|
||||
types into Other. There are a few reasons why we advice against this; firstly,
|
||||
we have found that it is preferred to match the conceptual model that your
|
||||
engineers have when describing your sofware. Secondly, Backstage helps your
|
||||
engineers manage their software by integrating the infratrucure tooling through
|
||||
plugins. Different plugins are used for managing different types of components.
|
||||
engineers have when describing your software. Secondly, Backstage helps your
|
||||
engineers manage their software by integrating the infrastructure tooling
|
||||
through plugins. Different plugins are used for managing different types of
|
||||
components.
|
||||
|
||||
For example, the
|
||||
[Lighthouse plugin](https://github.com/spotify/backstage/tree/master/plugins/lighthouse)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
id: software-catalog-overview
|
||||
title: Backstage Service Catalog (alpha)
|
||||
sidebar_label: Backstage Service Catalog
|
||||
---
|
||||
|
||||
## What is a Service Catalog?
|
||||
@@ -54,18 +55,18 @@ There are 3 ways to add components to the catalog:
|
||||
Users can register new components by going to `/create` and clicking the
|
||||
**REGISTER EXISTING COMPONENT** button:
|
||||
|
||||

|
||||

|
||||
|
||||
Backstage expects the full URL to the YAML in your source control. Example:
|
||||
|
||||
```
|
||||
```bash
|
||||
https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
|
||||
```
|
||||
|
||||
_More examples can be found
|
||||
[here](https://github.com/spotify/backstage/tree/master/packages/catalog-model/examples)._
|
||||
|
||||

|
||||

|
||||
|
||||
It is important to note that any kind of software can be registered in
|
||||
Backstage. Even if the software is not maintained by your company (SaaS
|
||||
@@ -78,12 +79,28 @@ All software created through the
|
||||
[Backstage Software Templates](../software-templates/index.md) are automatically
|
||||
registered in the catalog.
|
||||
|
||||
### Static catalog configuration
|
||||
|
||||
In addition to manually registering components, it is also possible to register
|
||||
components though [static configuration](../../conf/index.md). For example, the
|
||||
above example can be added using the following configuration:
|
||||
|
||||
```yaml
|
||||
catalog:
|
||||
locations:
|
||||
- type: github
|
||||
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
|
||||
```
|
||||
|
||||
More information about catalog configuration can be found
|
||||
[here](configuration.md).
|
||||
|
||||
### Updating component metadata
|
||||
|
||||
Teams owning the components are responsible for maintaining the metadata about
|
||||
them, and do so using their normal Git workflow.
|
||||
|
||||

|
||||

|
||||
|
||||
Once the change has been merged, Backstage will automatically show the updated
|
||||
metadata in the service catalog after a short while.
|
||||
@@ -92,25 +109,25 @@ metadata in the service catalog after a short while.
|
||||
|
||||
By default the service catalog shows components owned by the team of the logged
|
||||
in user. But you can also switch to _All_ to see all the components across your
|
||||
companie's software ecosystem. Basic inline _search_ and _column filtering_
|
||||
makes it easy to browse a big set of components.
|
||||
company's software ecosystem. Basic inline _search_ and _column filtering_ makes
|
||||
it easy to browse a big set of components.
|
||||
|
||||

|
||||

|
||||
|
||||
## Starring components
|
||||
|
||||
For easy and quick access to components you visit frequently, Backstage supports
|
||||
_starring_ of components:
|
||||
|
||||

|
||||

|
||||
|
||||
## Integrated tooling through plugins
|
||||
|
||||
The service catalog is a great way to organise the infrastructure tools you use
|
||||
The service catalog is a great way to organize the infrastructure tools you use
|
||||
to manage the software. This is how Backstage creates one developer portal for
|
||||
all your tools. Rather than asking teams to jump between different
|
||||
infrastructure UI’s (and incurring additional cognitive overhead each time they
|
||||
make a context switch), most of these tools can be organised around the entities
|
||||
infrastructure UIs (and incurring additional cognitive overhead each time they
|
||||
make a context switch), most of these tools can be organized around the entities
|
||||
in the catalog.
|
||||
|
||||

|
||||
|
||||
@@ -16,11 +16,13 @@ We model software in the Backstage catalogue using these three core entities
|
||||
(further explained below):
|
||||
|
||||
- **Components** are individual pieces of software
|
||||
|
||||
- **APIs** are the boundaries between different components
|
||||
|
||||
- **Resources** are physical or virtual infrastructure needed to operate a
|
||||
component
|
||||
|
||||

|
||||

|
||||
|
||||
### Component
|
||||
|
||||
@@ -29,22 +31,22 @@ backend service or data pipeline (list not exhaustive). A component can be
|
||||
tracked in source control, or use some existing open source or commercial
|
||||
software.
|
||||
|
||||
A component can implement APIs for other components to consume. In turn it
|
||||
might depend on APIs implemented by other components, or resources that are
|
||||
attached to it at runtime.
|
||||
A component can implement APIs for other components to consume. In turn it might
|
||||
depend on APIs implemented by other components, or resources that are attached
|
||||
to it at runtime.
|
||||
|
||||
### API
|
||||
|
||||
APIs form an important (maybe the most important) abstraction that allows large
|
||||
software ecosystems to scale. Thus, APIs are a first class citizen in the
|
||||
Backstage model and the primary way to discover existing functionality in
|
||||
the ecosystem.
|
||||
Backstage model and the primary way to discover existing functionality in the
|
||||
ecosystem.
|
||||
|
||||
APIs are implemented by components and form boundaries between components. They
|
||||
might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a data schema
|
||||
(eg Avro, TFRecord, ...), or as code interfaces. In any case, APIs exposed by
|
||||
components need to be in a known machine-readable format so we can
|
||||
build further tooling and analysis on top.
|
||||
might be defined using an RPC IDL (eg Protobuf, GraphQL, ...), a data schema (eg
|
||||
Avro, TFRecord, ...), or as code interfaces. In any case, APIs exposed by
|
||||
components need to be in a known machine-readable format so we can build further
|
||||
tooling and analysis on top.
|
||||
|
||||
APIs have a visibility: they are either public (making them available for any
|
||||
other component to consume), restricted (only available to a whitelisted set of
|
||||
@@ -62,12 +64,13 @@ footprint, and create tooling around them.
|
||||
|
||||
## Ecosystem Modeling
|
||||
|
||||
A large catalogue of components, APIs and resources can be highly granular
|
||||
and hard to understand as a whole. It might thus be convenient to further
|
||||
categorize these entities using the following (optional) concepts:
|
||||
* **Systems** are a collection of entities that cooperate to perform some
|
||||
function
|
||||
* **Domains** relate entities and systems to part of the business
|
||||
A large catalogue of components, APIs and resources can be highly granular and
|
||||
hard to understand as a whole. It might thus be convenient to further categorize
|
||||
these entities using the following (optional) concepts:
|
||||
|
||||
- **Systems** are a collection of entities that cooperate to perform some
|
||||
function
|
||||
- **Domains** relate entities and systems to part of the business
|
||||
|
||||
### System
|
||||
|
||||
@@ -82,15 +85,16 @@ exposes one or several public APIs. The main benefit of modelling a system is
|
||||
that it hides its resources and private APIs between the components for any
|
||||
consumers. This means that as the owner, you can evolve the implementation, in
|
||||
terms of components and resources, without your consumers being able to notice.
|
||||
Typically, a system will consist of at most a handful of components (see
|
||||
Domain for a grouping of systems).
|
||||
Typically, a system will consist of at most a handful of components (see Domain
|
||||
for a grouping of systems).
|
||||
|
||||
For example, a playlist management system might encapsulate a backend service
|
||||
to update playlists, a backend service to query them, and a database to store
|
||||
them. It could expose an RPC API, a daily snapshots dataset, and an event
|
||||
stream of playlist updates.
|
||||
For example, a playlist management system might encapsulate a backend service to
|
||||
update playlists, a backend service to query them, and a database to store them.
|
||||
It could expose an RPC API, a daily snapshots dataset, and an event stream of
|
||||
playlist updates.
|
||||
|
||||
### Domain
|
||||
|
||||
While systems are the basic level of encapsulation for related entities, it is
|
||||
often useful to group a collection of systems that share terminology, domain
|
||||
models, metrics, KPIs, business purpose, or documentation, i.e. they form a
|
||||
|
||||
@@ -58,7 +58,7 @@ Currently the catalog supports loading definitions from GitHub + Local Files. To
|
||||
load from other places, not only will there need to be another preparer, but the
|
||||
support to load the location will also need to be added to the Catalog.
|
||||
|
||||
For loading from a file the following command should work when the backend is
|
||||
For loading from a file, the following command should work when the backend is
|
||||
running:
|
||||
|
||||
```sh
|
||||
@@ -69,7 +69,7 @@ curl \
|
||||
--data-raw "{\"type\": \"file\", \"target\": \"${YOUR PATH HERE}/template.yaml\"}"
|
||||
```
|
||||
|
||||
If loading from a git location, you can run the following
|
||||
If loading from a Git location, you can run the following
|
||||
|
||||
```sh
|
||||
curl \
|
||||
@@ -83,7 +83,7 @@ This should then have added the catalog, and also should now be listed under the
|
||||
create page at http://localhost:3000/create.
|
||||
|
||||
Alternatively, if you want to get setup with some mock templates that are
|
||||
already provided for you, you can run the following to load those templates:
|
||||
already provided, run the following to load those templates:
|
||||
|
||||
```
|
||||
yarn lerna run mock-data
|
||||
|
||||
@@ -7,8 +7,8 @@ Publishers are responsible for pushing and storing the templated skeleton after
|
||||
the values have been templated by the `Templater`. See
|
||||
[Create your own templater](./create-your-own-templater.md) for more info.
|
||||
|
||||
They recieve a directory or location where the templater has sucessfully run on,
|
||||
and is now ready to store somewhere. They also get given some other options
|
||||
They receive a directory or location where the templater has sucessfully run
|
||||
and is now ready to store somewhere. They also are given some other options
|
||||
which are sent from the frontend, such as the `storePath` which is a string of
|
||||
where the frontend thinks we should save this templated folder.
|
||||
|
||||
@@ -17,7 +17,7 @@ Currently we provide the following `publishers`:
|
||||
- `github`
|
||||
|
||||
This publisher is passed through to the `createRouter` function of the
|
||||
`@spotify/plugin-scaffolder-backend`. Currently only one publisher is supported,
|
||||
`@spotify/plugin-scaffolder-backend`. Currently, only one publisher is supported,
|
||||
but PR's are always welcome.
|
||||
|
||||
An full example backend can be found
|
||||
|
||||
@@ -8,14 +8,14 @@ returned by the preparers, and then executing the templating command on top of
|
||||
the file and returning the completed template path. This may or may not be the
|
||||
same directory as the input directory.
|
||||
|
||||
They also recieve additional values from the frontend, which can be used to
|
||||
They also receive additional values from the frontend, which can be used to
|
||||
interpolate into the skeleton files.
|
||||
|
||||
Currently we provide the following templaters:
|
||||
|
||||
- `cookiecutter`
|
||||
|
||||
This templater is added the `TemplaterBuilder` and then passed into the
|
||||
This templater is added to the `TemplaterBuilder` and then passed into the
|
||||
`createRouter` function of the `@spotify/plugin-scaffolder-backend`
|
||||
|
||||
An full example backend can be found
|
||||
@@ -48,7 +48,7 @@ This `TemplaterKey` is used to select the correct templater from the
|
||||
`spec.templater` in the
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template).
|
||||
|
||||
If you wish to add a new templater you'll need to register it with the
|
||||
If you wish to add a new templater, you'll need to register it with the
|
||||
`TemplaterBuilder`.
|
||||
|
||||
### Creating your own Templater to add to the `TemplaterBuilder`
|
||||
@@ -83,10 +83,10 @@ follows:
|
||||
- `dockerClient` - a [dockerode](https://github.com/apocas/dockerode) client to
|
||||
be able to run docker containers.
|
||||
|
||||
_note_ currently the templaters that we provide are basically docker action
|
||||
_note_ Currently the templaters that we provide are basically Docker action
|
||||
containers that are run on top of the skeleton folder. This keeps dependencies
|
||||
to a minimal for running backstage scaffolder, but you don't /have/ to use
|
||||
docker. You could create your own templater that spins up an EC2 instance and
|
||||
Docker. You could create your own templater that spins up an EC2 instance and
|
||||
downloads the folder and does everything using an AMI if you want. It's entirely
|
||||
up to you!
|
||||
|
||||
@@ -138,7 +138,7 @@ spec:
|
||||
description: Description of the component
|
||||
```
|
||||
|
||||
You see that the `spec.templater` is set as `handlebars`, you'll need to
|
||||
You see that the `spec.templater` is set as `handlebars`, so you'll need to
|
||||
register this with the `TemplaterBuilder` like so:
|
||||
|
||||
```ts
|
||||
|
||||
@@ -5,11 +5,11 @@ title: Extending the Scaffolder
|
||||
|
||||
Welcome. Take a seat. You're at the Scaffolder Documentation.
|
||||
|
||||
So - You wanna create stuff inside your company from some prebaked templates?
|
||||
So, you want to create stuff inside your company from some prebaked templates?
|
||||
You're at the right place.
|
||||
|
||||
This guide is gonna take you through how the Scaffolder in Backstage works.
|
||||
We'll dive into some jargon and run through whats going on in the backend to be
|
||||
This guide is going to take you through how the Scaffolder in Backstage works.
|
||||
We'll dive into some jargon and run through what's going on in the backend to be
|
||||
able to create these templates. There's also more guides that you might find
|
||||
useful at the bottom of this document. At it's core, theres 3 simple stages.
|
||||
|
||||
@@ -25,9 +25,9 @@ scaffolder that you will need to know:
|
||||
3. Publish
|
||||
|
||||
Each of these steps can be configured for your own use case, but we provide some
|
||||
sensible defaults too.
|
||||
sensible defaults, too.
|
||||
|
||||
Lets dive a little deeper into these phases.
|
||||
Let's dive a little deeper into these phases.
|
||||
|
||||
### Glossary and Jargon
|
||||
|
||||
@@ -38,8 +38,8 @@ the router to pick the correct `Preparer` to run for the `Template` entity.
|
||||
|
||||
**Templater** - The templater is responsible for actually running the chosen
|
||||
templater on top of the previously returned temporary directory from the
|
||||
**Preprarer**. We advise making these docker containers as it can keep all
|
||||
dependencies, for example Cookiecutter, self contained and not a dependency on
|
||||
**Preprarer**. We advise making these Docker containers as it can keep all
|
||||
dependencies--for example Cookiecutter--self contained and not a dependency on
|
||||
the host machine.
|
||||
|
||||
**Publisher** - The publisher is responsible for taking the finished directory,
|
||||
@@ -50,11 +50,11 @@ passed through to the scaffolder backend.
|
||||
|
||||
### How it works
|
||||
|
||||
The main of the heavy lifting is done in the
|
||||
Most of the heavy lifting is done in the
|
||||
[router.ts](https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/service/router.ts#L93)
|
||||
file in the `scaffolder-backend` plugin.
|
||||
|
||||
There are 2 routes defined in the router. `POST /v1/jobs` and
|
||||
There are two routes defined in the router: `POST /v1/jobs` and
|
||||
`GET /v1/job/:jobId`
|
||||
|
||||
To create a scaffolding job, a JSON object containing the
|
||||
@@ -78,7 +78,7 @@ additional templating values must be posted as the post body.
|
||||
The values should represent something that is valid with the `schema` part of
|
||||
the [Template Entity](../../software-catalog/descriptor-format.md#kind-template)
|
||||
|
||||
Once that has been posted, a job will be setup with different stages. And the
|
||||
Once that has been posted, a job will be setup with different stages, and the
|
||||
job processor will complete each stage before moving onto the next stage, whilst
|
||||
collecting logs and mutating the running job.
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ title: Software Templates
|
||||
---
|
||||
|
||||
The Software Templates part of Backstage is a tool that can help you create
|
||||
Components inside Backstage. It by default has the ability to load skeletons of
|
||||
code, template in some variables and then publish the template to some location
|
||||
Components inside Backstage. By default, it has the ability to load skeletons of
|
||||
code, template in some variables, and then publish the template to some location
|
||||
like GitHub.
|
||||
|
||||
<video width="100%" height="100%" controls>
|
||||
@@ -33,8 +33,8 @@ internally.
|
||||

|
||||
|
||||
After filling in these variables, you'll get some more fields to fill out which
|
||||
are required for backstage usage. The owner, which is a `user` in the backstage
|
||||
system, and the `storePath` which right now must be a GitHub Organisation and a
|
||||
are required for backstage usage: the owner, (which is a `user` in the backstage
|
||||
system), the `storePath` (which right now must be a GitHub Organisation), and a
|
||||
non-existing github repository name in the format `organisation/reponame`.
|
||||
|
||||

|
||||
@@ -51,13 +51,13 @@ It shouldn't take too long, and you'll have a success screen!
|
||||

|
||||
|
||||
If it fails, you'll be able to click on each section to get the log from the
|
||||
step that failed which can be helpful to debug.
|
||||
step that failed which can be helpful in debugging.
|
||||
|
||||

|
||||
|
||||
### View Component in Catalog
|
||||
|
||||
When it's been created you'll see the `View in Catalog` button, which will take
|
||||
When it's been created, you'll see the `View in Catalog` button, which will take
|
||||
you to the registered component in the catalog:
|
||||
|
||||

|
||||
|
||||
@@ -101,3 +101,7 @@ more to come...
|
||||
https://github.com/spotify/backstage/blob/master/packages/techdocs-container
|
||||
[techdocs/cli]:
|
||||
https://github.com/spotify/backstage/blob/master/packages/techdocs-cli
|
||||
|
||||
## TechDocs Big Picture
|
||||
|
||||

|
||||
|
||||
@@ -53,4 +53,4 @@ Reader. The reason why transformers were introduced was to provide a way to
|
||||
transform the HTML content on pre and post render (e.g. rewrite docs links or
|
||||
modify css).
|
||||
|
||||
[Transformers API docs](https://github.com/spotify/backstage/blob/master/plugins/techdocs/src/reader/transformers/README.md)
|
||||
[Transformers API docs](https://github.com/spotify/backstage/blob/master/plugins/techdocs/src/reader/README.md)
|
||||
|
||||
@@ -30,6 +30,11 @@ the documentation template.
|
||||
Create an entity from the documentation template and you will get the needed
|
||||
setup for free.
|
||||
|
||||
!!! warning Currently the Backstage Software Templates are limited to create repositories
|
||||
inside GitHub organizations. You also need to generate an personal access token
|
||||
and use as an environment variable. Read more about this
|
||||
[here](../software-templates/installation.md#runtime-dependencies).
|
||||
|
||||
### Manually add documentation setup to already existing repository
|
||||
|
||||
Prerequisities:
|
||||
|
||||
@@ -85,7 +85,3 @@ yarn create-plugin # Create a new plugin
|
||||
> See
|
||||
> [package.json](https://github.com/spotify/backstage/blob/master/package.json)
|
||||
> for other yarn commands/options.
|
||||
|
||||
[Next Step - Create a Backstage plugin](../plugins/create-a-plugin.md)
|
||||
|
||||
[Back to Docs](../README.md)
|
||||
|
||||
@@ -3,9 +3,22 @@ id: index
|
||||
title: Running Backstage Locally
|
||||
---
|
||||
|
||||
First make sure you are using NodeJS with an Active LTS Release, currently v12.
|
||||
This is made easy with a version manager such as nvm which allows for version switching.
|
||||
|
||||
```bash
|
||||
# Checking your version
|
||||
node --version
|
||||
> v14.7.0
|
||||
|
||||
# Adding a second node version
|
||||
nvm install 12
|
||||
> Downloading and installing node v12.18.3...
|
||||
> Now using node v12.18.3 (npm v6.14.6)
|
||||
```
|
||||
|
||||
To get up and running with a local Backstage to evaluate it, let's clone it off
|
||||
of GitHub and run an initial build. First make sure that you have at least node
|
||||
version 12 installed locally.
|
||||
of GitHub and run an initial build.
|
||||
|
||||
```bash
|
||||
# Start from your local development folder
|
||||
|
||||
@@ -87,14 +87,17 @@ Example of tactics we have used to evangelize Backstage internally:
|
||||
- Pro-actively identify new plugins. Reach out to teams that own internal UIs or
|
||||
platforms that you think would make sense to consolidate into Backstage.
|
||||
|
||||
## Metrics
|
||||
## KPIs and metrics
|
||||
|
||||
These are some of the metrics that you can use to verify if Backstage has a
|
||||
successful impact on your software development process:
|
||||
|
||||
- **Onboarding time** Time until new engineers are productive. At Spotify we
|
||||
measure this as the time until the employee has merged their 10th PR (this
|
||||
metric was down 55% two years after deploying Backstage).
|
||||
metric was down 55% two years after deploying Backstage). Even though you may
|
||||
not be onboarding engineers at a rapid pace, this metric is a great proxy for
|
||||
the overall complexity of your ecosystem. Reducing it will therefore benefit
|
||||
your whole engineering organization, not just new joiners.
|
||||
|
||||
- **Number of merges per developer/day** Less time spent jumping between
|
||||
different tools and looking for information means more time to focus on
|
||||
@@ -142,3 +145,16 @@ Backstage as _the_ platform:
|
||||
|
||||
- % of contributions coming from outside the central Backstage team (currently
|
||||
85% inside Spotify)
|
||||
|
||||
- Traditional metrics such as visits (MAU, DAU, etc) and page views. Currently
|
||||
~50% of all Spotifiers use Backstage on a monthly basis, even though the
|
||||
percentage of engineers is below 50%. Most engineers actually use Backstage on
|
||||
a daily basis.
|
||||
|
||||
Again, any feedback is appreciated. Please use the Edit button at the top of the
|
||||
page to make a suggestion.
|
||||
|
||||
_**Note!** It might be tempting to try to optimize Backstage usage and
|
||||
"engagement". Even though you want to consolidate all your tooling and technical
|
||||
documentation in Backstage, it is important to remember that time spent in
|
||||
Backstage is time not spent writing code_ 🙃
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
id: background
|
||||
title: The Spotify Story
|
||||
---
|
||||
|
||||
Backstage was born out of necessity at Spotify. We found that as we grew, our
|
||||
infrastructure was becoming more fragmented, our engineers less productive.
|
||||
|
||||
Instead of building and testing code, teams were spending more time looking for
|
||||
the right information just to get started. “Where’s the API for that service
|
||||
we’re all supposed to be using?” “What version of that framework is everyone
|
||||
on?” “This service isn’t responding, who owns it?” “I can’t find documentation
|
||||
for anything!”
|
||||
|
||||
Context switching and cognitive overload were dragging engineers down, day by
|
||||
day. We needed to make it easier for our engineers to do their work without
|
||||
having to become an expert in every aspect of infrastructure tooling.
|
||||
|
||||
Our idea was to centralize and simplify end-to-end software development with an
|
||||
abstraction layer that sits on top of all of our infrastructure and developer
|
||||
tooling. That’s Backstage.
|
||||
|
||||
It’s a developer portal powered by a centralized service catalog — with a plugin
|
||||
architecture that makes it endlessly extensible and customizable.
|
||||
|
||||
Manage all your services, software, tooling, and testing in Backstage. Start
|
||||
building a new microservice using an automated template in Backstage. Create,
|
||||
maintain, and find the documentation for all that software in Backstage.
|
||||
|
||||
One place for everything. Accessible to everyone.
|
||||
@@ -18,12 +18,15 @@ Out of the box, Backstage includes:
|
||||
- [Backstage Service Catalog](../features/software-catalog/index.md) for
|
||||
managing all your software (microservices, libraries, data pipelines,
|
||||
websites, ML models, etc.)
|
||||
|
||||
- [Backstage Software Templates](../features/software-templates/index.md) for
|
||||
quickly spinning up new projects and standardizing your tooling with your
|
||||
organization’s best practices
|
||||
|
||||
- [Backstage TechDocs](../features/techdocs/README.md) for making it easy to
|
||||
create, maintain, find, and use technical documentation, using a "docs like
|
||||
code" approach
|
||||
|
||||
- Plus, a growing ecosystem of
|
||||
[open source plugins](https://github.com/spotify/backstage/tree/master/plugins)
|
||||
that further expand Backstage’s customizability and functionality
|
||||
@@ -33,12 +36,15 @@ Out of the box, Backstage includes:
|
||||
- For _engineering managers_, it allows you to maintain standards and best
|
||||
practices across the organization, and can help you manage your whole tech
|
||||
ecosystem, from migrations to test certification.
|
||||
|
||||
- For _end users_ (developers), it makes it fast and simple to build software
|
||||
components in a standardized way, and it provides a central place to manage
|
||||
all projects and documentation.
|
||||
|
||||
- For _platform engineers_, it enables extensibility and scalability by letting
|
||||
you easily integrate new tools and services (via plugins), as well as
|
||||
extending the functionality of existing ones.
|
||||
|
||||
- For _everyone_, it’s a single, consistent experience that ties all your
|
||||
infrastructure tooling, resources, standards, owners, contributors, and
|
||||
administrators together in one place.
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
id: add-to-marketplace
|
||||
title: Add to Marketplace
|
||||
---
|
||||
|
||||
## Adding a Plugin to the Marketplace
|
||||
|
||||
To add a new plugin to the [plugin marketplace](https://backstage.io/plugins)
|
||||
create a file in `data/plugins` with your plugin's information. Example:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Your Plugin
|
||||
author: Your Name
|
||||
authorUrl: # A link to information about the author E.g. Company url, github user profile, etc
|
||||
category: Monitoring # A single category e.g. CI, Machine Learning, Services, Monitoring
|
||||
description: A brief description of the plugin. # Max 170 characters
|
||||
documentation: # A link to your documentation E.g. Your github README
|
||||
iconUrl: # Used as the src attribute for your logo.
|
||||
# You can provide an external url or add your logo under static/img and provide a path
|
||||
# relative to static/ e.g. img/my-logo.png
|
||||
npmPackageName: # Your npm package name E.g. '@backstage/plugin-<etc>' quotes are required
|
||||
```
|
||||
@@ -71,11 +71,7 @@ Example:
|
||||
```yaml
|
||||
# In app-config.yaml
|
||||
proxy:
|
||||
'/frobs':
|
||||
target: 'http://api.frobsco.com/v1'
|
||||
changeOrigin: true
|
||||
pathRewrite:
|
||||
'^/proxy/frobs/': '/'
|
||||
'/frobs': http://api.frobsco.com/v1
|
||||
```
|
||||
|
||||
```ts
|
||||
@@ -86,9 +82,8 @@ fetch(`${backendUrl}/proxy/frobs/list`)
|
||||
.then(payload => setFrobs(payload as Frob[]));
|
||||
```
|
||||
|
||||
The proxy is powered by the `http-proxy-middleware` package, and supports all of
|
||||
its
|
||||
[configuration options](https://github.com/chimurai/http-proxy-middleware#options).
|
||||
The proxy is powered by the `http-proxy-middleware` package. See
|
||||
[Proxying](proxying.md) for a full description of its configuration options.
|
||||
|
||||
Internally at Spotify, the proxy option has been the overwhelmingly most popular
|
||||
choice for plugin makers. Since we have DNS based service discovery in place and
|
||||
|
||||
@@ -15,20 +15,16 @@ dependencies, then run the following on your command line (invoking the
|
||||
yarn create-plugin
|
||||
```
|
||||
|
||||
<p align='center'>
|
||||
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/create-plugin_output.png' width='600' alt='create plugin'>
|
||||
</p>
|
||||

|
||||
|
||||
This will create a new Backstage Plugin based on the ID that was provided. It
|
||||
will be built and added to the Backstage App automatically.
|
||||
|
||||
_If `yarn start` is already running you should be able to see the default page
|
||||
for your new plugin directly by navigating to
|
||||
`http://localhost:3000/my-plugin`._
|
||||
> If `yarn start` is already running you should be able to see the default page
|
||||
> for your new plugin directly by navigating to
|
||||
> `http://localhost:3000/my-plugin`.
|
||||
|
||||
<p align='center'>
|
||||
<img src='https://github.com/spotify/backstage/raw/master/docs/plugins/my-plugin_screenshot.png' width='600' alt='my plugin'>
|
||||
</p>
|
||||

|
||||
|
||||
You can also serve the plugin in isolation by running `yarn start` in the plugin
|
||||
directory. Or by using the yarn workspace command, for example:
|
||||
@@ -40,7 +36,3 @@ yarn workspace @backstage/plugin-welcome start # Also supports --check
|
||||
This method of serving the plugin provides quicker iteration speed and a faster
|
||||
startup and hot reloads. It is only meant for local development, and the setup
|
||||
for it can be found inside the plugin's `dev/` directory.
|
||||
|
||||
[Next Step - Structure of a plugin](structure-of-a-plugin.md)
|
||||
|
||||
[Back to Getting Started](../README.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: plugin-development
|
||||
title: Plugin Development in Backstage
|
||||
title: Plugin Development
|
||||
---
|
||||
|
||||
Backstage plugins provide features to a Backstage App.
|
||||
@@ -10,28 +10,12 @@ type of content. Plugins all use a common set of platform APIs and reusable UI
|
||||
components. Plugins can fetch data from external sources using the regular
|
||||
browser APIs or by depending on external modules to do the work.
|
||||
|
||||
<!-- MOVED TO create-a-plugin.md ## Creating a new plugin
|
||||
On your command line, invoke the `backstage-cli` to create a new plugin:
|
||||
```bash
|
||||
yarn create-plugin
|
||||
```
|
||||
|
||||

|
||||
|
||||
This will create a new Backstage Plugin based on the ID that was provided. It will be built and
|
||||
added to the Backstage App automatically.
|
||||
|
||||
*If `yarn start` is already running you should be able to see the default page for your new
|
||||
plugin directly by navigating to `http://localhost:3000/my-plugin`.*
|
||||
|
||||
 -->
|
||||
|
||||
## Developing guidelines
|
||||
|
||||
- Consider writing plugins in `TypeScript`.
|
||||
- Plan the directory structure of your plugin so that it becomes easy to manage.
|
||||
- Prefer using the Backstage components, otherwise go with
|
||||
[Material-UI](https://material-ui.com/).
|
||||
- Prefer using the [Backstage components](https://backstage.io/storybook),
|
||||
otherwise go with [Material-UI](https://material-ui.com/).
|
||||
- Check out the shared Backstage APIs before building a new one.
|
||||
|
||||
## Plugin concepts / API
|
||||
|
||||
@@ -3,4 +3,73 @@ id: proxying
|
||||
title: Proxying
|
||||
---
|
||||
|
||||
## TODO
|
||||
## Overview
|
||||
|
||||
The Backstage backend comes packaged with a basic HTTP proxy, that can aid in
|
||||
reaching backend service APIs from frontend plugin code. See
|
||||
[Call Existing API](call-existing-api.md) for a description of when the proxy
|
||||
can be the best choice for communicating with an API.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The plugin is already added to a default Backstage project.
|
||||
|
||||
In `packages/backend/src/index.ts`:
|
||||
|
||||
```ts
|
||||
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
.loadConfig(configReader)
|
||||
/** ... other routers ... */
|
||||
.addRouter('/proxy', await proxy(proxyEnv, '/proxy'));
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration for the proxy plugin lives under a `proxy` root key of your
|
||||
`app-config.yaml` file.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
# in app-config.yaml
|
||||
proxy:
|
||||
'/simple-example': http://simple.example.com:8080
|
||||
'/larger-example/v1':
|
||||
target: http://larger.example.com:8080/svc.v1
|
||||
headers:
|
||||
Authorization:
|
||||
$secret:
|
||||
env: EXAMPLE_AUTH_HEADER
|
||||
```
|
||||
|
||||
Each key under the proxy configuration entry is a route to match, below the
|
||||
prefix that the proxy plugin is mounted on. It must start with a slash. For
|
||||
example, if the backend mounts the proxy plugin as `/proxy`, the above
|
||||
configuration will lead to the proxy acting on backend requests to
|
||||
`/proxy/simple-example/...` and `/proxy/larger-example/v1/...`.
|
||||
|
||||
The value inside each route is either a simple URL string, or an object on the
|
||||
format accepted by
|
||||
[http-proxy-middleware](https://www.npmjs.com/package/http-proxy-middleware).
|
||||
|
||||
If the value is a string, it is assumed to correspond to:
|
||||
|
||||
```yaml
|
||||
target: <the string>
|
||||
changeOrigin: true
|
||||
pathRewrite:
|
||||
'^<url prefix><the string>/': '/'
|
||||
```
|
||||
|
||||
When the target is an object, it is given verbatim to `http-proxy-middleware`
|
||||
except with the following caveats for convenience:
|
||||
|
||||
- If `changeOrigin` is not specified, it is set to `true`. This is the most
|
||||
commonly useful value.
|
||||
- If `pathRewrite` is not specified, it is set to a single rewrite that removes
|
||||
the entire prefix and route. In the above example, a rewrite of
|
||||
`'^/proxy/larger-example/v1/': '/'` is added. That means that a request to
|
||||
`/proxy/larger-example/v1/some/path` will be translated to a request to
|
||||
`http://larger.example.com:8080/svc.v1/some/path`.
|
||||
|
||||
@@ -37,5 +37,3 @@ const myPluginRouteRef = createRouteRef({
|
||||
title: 'My Plugin',
|
||||
});
|
||||
```
|
||||
|
||||
[Back to References](../README.md)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
id: index
|
||||
title: Overview
|
||||
---
|
||||
|
||||
## Coming soon!
|
||||
@@ -1,12 +1,15 @@
|
||||
# Purpose
|
||||
---
|
||||
id: journey
|
||||
title: Future developer journey
|
||||
---
|
||||
|
||||
This RFC describes a possible journey of a future Backstage plugin developer as
|
||||
they build a plugin that touches many different aspects of a Backstage. The
|
||||
story invents many new things that are not part of Backstage today, but are
|
||||
things that I'm suggesting we should add as long term or north star goals. The
|
||||
idea is to discuss what parts of the story makes sense to aim for, and what we'd
|
||||
want to do differently or not at all. The "chapters" are numbered to make it a
|
||||
bit easier to comment on parts of the story.
|
||||
> This document describes a possible journey of a **_future_** Backstage plugin
|
||||
> developer as they build a plugin that touches many different aspects of a
|
||||
> Backstage. The story invents many new things that are not part of Backstage
|
||||
> today, but are things that I'm suggesting we should add as long term or north
|
||||
> star goals. The idea is to discuss what parts of the story makes sense to aim
|
||||
> for, and what we'd want to do differently or not at all. The "chapters" are
|
||||
> numbered to make it a bit easier to comment on parts of the story.
|
||||
|
||||
# The Protagonist
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
| app.resources | Kubernetes Pod resource requests/limits | `{}` |
|
||||
| app.nodeSelector | Node selectors for scheduling app/frontend pods | `{}` |
|
||||
| app.tolerations | Tolerations for scheduling app/frontend pods | `{}` |
|
||||
| app.affinity | Affinity setttings for scheduling app/frontend pods | `{}` |
|
||||
| app.affinity | Affinity settings for scheduling app/frontend pods | `{}` |
|
||||
|
||||
## Backend Values
|
||||
|
||||
@@ -48,4 +48,4 @@
|
||||
| backend.resources | Kubernetes Pod resource requests/limits | `{}` |
|
||||
| backend.nodeSelector | Node selectors for scheduling backend pods | `{}` |
|
||||
| backend.tolerations | Tolerations for scheduling backend pods | `{}` |
|
||||
| backend.affinity | Affinity setttings for scheduling backend pods | `{}` |
|
||||
| backend.affinity | Affinity settings for scheduling backend pods | `{}` |
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
"packages": ["packages/*", "plugins/*"],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.1.1-alpha.18"
|
||||
"version": "0.1.1-alpha.20"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: CircleCI
|
||||
author: Spotify
|
||||
authorUrl: https://www.spotify.com/
|
||||
category: CI
|
||||
description: Automate your development process with CI hosted in the cloud or on a private server.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/circleci
|
||||
iconUrl: https://d3r49iyjzglexf.cloudfront.net/logo-wordmark-26f8eaea9b0f6e13b90d3f4a8fd8fda31490f5af41daab98bbede45037682576.svg
|
||||
npmPackageName: '@backstage/plugin-circleci'
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: GitOps Clusters
|
||||
author: Weaveworks
|
||||
authorUrl: https://www.weave.works/
|
||||
category: Kubernetes
|
||||
description: Create GitOps-managed Kubernetes clusters. Currently, it supports provisioning EKS clusters on GitHub via GitHub Actions.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/gitops-profiles
|
||||
iconUrl: https://res-5.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_256,w_256,f_auto,q_auto:eco/v1462316670/i9d3delzvx1erzjhmcws.png
|
||||
npmPackageName: '@backstage/plugin-gitops-profiles'
|
||||
tags:
|
||||
- kubernetes
|
||||
- gitops
|
||||
- github
|
||||
- eks
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: GraphiQL
|
||||
author: Spotify
|
||||
authorUrl: https://www.spotify.com/
|
||||
category: Debugging
|
||||
description: Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse
|
||||
iconUrl: https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/GraphQL_Logo.svg/1024px-GraphQL_Logo.svg.png
|
||||
npmPackageName: '@backstage/plugin-graphiql'
|
||||
tags:
|
||||
- graphql
|
||||
- github
|
||||
- gitlab
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Lighthouse
|
||||
author: Spotify
|
||||
authorUrl: https://www.spotify.com/
|
||||
category: Accessibility
|
||||
description: Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/lighthouse
|
||||
iconUrl: https://seeklogo.com/images/G/google-lighthouse-logo-1C7FA08580-seeklogo.com.png
|
||||
npmPackageName: '@backstage/plugin-lighthouse'
|
||||
tags:
|
||||
- web
|
||||
- seo
|
||||
- accessibility
|
||||
- performance
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: New Relic
|
||||
author: '@timwheelercom'
|
||||
authorUrl: https://github.com/timwheelercom
|
||||
category: Monitoring
|
||||
description: Observability platform built to help engineers create and monitor their software.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/newrelic
|
||||
iconUrl: https://www.mulesoft.com/sites/default/files/2018-10/New_relic.png
|
||||
npmPackageName: '@backstage/plugin-newrelic'
|
||||
tags:
|
||||
- performance
|
||||
- monitoring
|
||||
- errors
|
||||
- alerting
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Rollbar
|
||||
author: '@andrewthauer'
|
||||
authorUrl: https://github.com/andrewthauer
|
||||
category: Monitoring
|
||||
description: View Rollbar errors for your services in Backstage.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/rollbar
|
||||
iconUrl: https://rollbar.com/assets/media/rollbar-mark-color.png
|
||||
npmPackageName: '@backstage/plugin-rollbar'
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Sentry
|
||||
author: Spotify
|
||||
authorUrl: https://www.spotify.com/
|
||||
category: Monitoring
|
||||
description: View Sentry issues in Backstage.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/sentry
|
||||
iconUrl: https://sentry-brand.storage.googleapis.com/sentry-glyph-white.png
|
||||
npmPackageName: '@backstage/plugin-sentry'
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Tech Radar
|
||||
author: Spotify
|
||||
authorUrl: https://www.spotify.com/
|
||||
category: Discovery
|
||||
description: Visualize the your company's official guidelines of different areas of software development.
|
||||
documentation: https://github.com/spotify/backstage/tree/master/plugins/tech-radar
|
||||
iconUrl: https://github.com/spotify/backstage/raw/master/plugins/tech-radar/docs/screenshot.png
|
||||
npmPackageName: '@backstage/plugin-tech-radar'
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Travis CI
|
||||
author: roadie.io
|
||||
authorUrl: https://roadie.io/
|
||||
category: CI
|
||||
description: View Travis CI builds for your service in Backstage.
|
||||
documentation: https://roadie.io/backstage/plugins/travis-ci
|
||||
iconUrl: https://roadie.io/static/af2941eaf0af675facb281d566f42e14/45f2b/travis-ci-mascot-200x200.png
|
||||
npmPackageName: '@roadiehq/backstage-plugin-travis-ci'
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"title": "Adding authentication providers"
|
||||
},
|
||||
"auth/auth-backend-classes": {
|
||||
"title": "auth/auth-backend-classes"
|
||||
"title": "Auth backend classes"
|
||||
},
|
||||
"auth/auth-backend": {
|
||||
"title": "Auth backend"
|
||||
@@ -103,7 +103,8 @@
|
||||
"title": "External integrations"
|
||||
},
|
||||
"features/software-catalog/software-catalog-overview": {
|
||||
"title": "Backstage Service Catalog (alpha)"
|
||||
"title": "Backstage Service Catalog (alpha)",
|
||||
"sidebar_label": "Backstage Service Catalog"
|
||||
},
|
||||
"features/software-catalog/installation": {
|
||||
"title": "features/software-catalog/installation"
|
||||
@@ -174,9 +175,6 @@
|
||||
"getting-started/installation": {
|
||||
"title": "Installation"
|
||||
},
|
||||
"journey": {
|
||||
"title": "journey"
|
||||
},
|
||||
"overview/adopting": {
|
||||
"title": "Strategies for adopting"
|
||||
},
|
||||
@@ -186,6 +184,9 @@
|
||||
"overview/architecture-terminology": {
|
||||
"title": "Architecture terminology"
|
||||
},
|
||||
"overview/background": {
|
||||
"title": "The Spotify Story"
|
||||
},
|
||||
"overview/roadmap": {
|
||||
"title": "Project roadmap"
|
||||
},
|
||||
@@ -198,6 +199,9 @@
|
||||
"overview/what-is-backstage": {
|
||||
"title": "What is Backstage?"
|
||||
},
|
||||
"plugins/add-to-marketplace": {
|
||||
"title": "Add to Marketplace"
|
||||
},
|
||||
"plugins/backend-plugin": {
|
||||
"title": "Backend plugin"
|
||||
},
|
||||
@@ -214,7 +218,7 @@
|
||||
"title": "Intro to plugins"
|
||||
},
|
||||
"plugins/plugin-development": {
|
||||
"title": "Plugin Development in Backstage"
|
||||
"title": "Plugin Development"
|
||||
},
|
||||
"plugins/proxying": {
|
||||
"title": "Proxying"
|
||||
@@ -285,16 +289,16 @@
|
||||
"reference/utility-apis/StorageApi": {
|
||||
"title": "reference/utility-apis/StorageApi"
|
||||
},
|
||||
"tutorials/index": {
|
||||
"title": "Overview"
|
||||
"tutorials/journey": {
|
||||
"title": "Future developer journey"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"GitHub": "GitHub",
|
||||
"Docs": "Docs",
|
||||
"Plugins": "Plugins",
|
||||
"Blog": "Blog",
|
||||
"Demos": "Demos",
|
||||
"The Spotify story": "The Spotify story",
|
||||
"Newsletter": "Newsletter"
|
||||
},
|
||||
"categories": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"name": "backstage-microsite",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
@@ -13,6 +13,7 @@
|
||||
"rename-version": "docusaurus-rename-version"
|
||||
},
|
||||
"devDependencies": {
|
||||
"docusaurus": "^2.0.0-alpha.61"
|
||||
"docusaurus": "^2.0.0-alpha.61",
|
||||
"js-yaml": "^3.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
const Components = require(`${process.cwd()}/core/Components.js`);
|
||||
const Block = Components.Block;
|
||||
const Breakpoint = Components.Breakpoint;
|
||||
|
||||
const Background = props => {
|
||||
const { config: siteConfig } = props;
|
||||
const { baseUrl } = siteConfig;
|
||||
return (
|
||||
<div className="mainWrapper">
|
||||
<Block>
|
||||
<Block.Container column>
|
||||
<Block.TitleBox story>The Spotify Story</Block.TitleBox>
|
||||
<Block.TextBox>
|
||||
<Block.Paragraph>
|
||||
Backstage was born out of necessity at Spotify. We found that as
|
||||
we grew, our infrastructure was becoming more fragmented, our
|
||||
engineers less productive.{' '}
|
||||
</Block.Paragraph>
|
||||
|
||||
<Block.Paragraph>
|
||||
Instead of building and testing code, teams were spending more
|
||||
time looking for the right information just to get started.
|
||||
“Where’s the API for that service we’re all supposed to be using?”
|
||||
“What version of that framework is everyone on?” “This service
|
||||
isn’t responding, who owns it?” “I can’t find documentation for
|
||||
anything!”{' '}
|
||||
</Block.Paragraph>
|
||||
<Breakpoint
|
||||
narrow={
|
||||
<Block small>
|
||||
<Block.QuoteContainer>
|
||||
<Block.Divider quote />
|
||||
|
||||
<Block.Quote>
|
||||
One place for everything. Accessible to everyone.
|
||||
</Block.Quote>
|
||||
</Block.QuoteContainer>
|
||||
</Block>
|
||||
}
|
||||
></Breakpoint>
|
||||
<Block.Paragraph>
|
||||
Context switching and cognitive overload were dragging engineers
|
||||
down, day by day. We needed to make it easier for our engineers to
|
||||
do their work without having to become an expert in every aspect
|
||||
of infrastructure tooling.
|
||||
</Block.Paragraph>
|
||||
|
||||
<Block.Paragraph>
|
||||
Our idea was to centralize and simplify end-to-end software
|
||||
development with an abstraction layer that sits on top of all of
|
||||
our infrastructure and developer tooling. That’s Backstage.
|
||||
</Block.Paragraph>
|
||||
|
||||
<Block.Paragraph>
|
||||
It’s a developer portal powered by a centralized service catalog —
|
||||
with a plugin architecture that makes it endlessly extensible and
|
||||
customizable.
|
||||
</Block.Paragraph>
|
||||
|
||||
<Block.Paragraph>
|
||||
Manage all your services, software, tooling, and testing in
|
||||
Backstage. Start building a new microservice using an automated
|
||||
template in Backstage. Create, maintain, and find the
|
||||
documentation for all that software in Backstage.{' '}
|
||||
</Block.Paragraph>
|
||||
|
||||
<Block.Paragraph>
|
||||
One place for everything. Accessible to everyone.
|
||||
</Block.Paragraph>
|
||||
<Breakpoint
|
||||
narrow={
|
||||
<Block small>
|
||||
<Block.LinkButton stretch href={'https://backstage.io/'}>
|
||||
Explore Features
|
||||
</Block.LinkButton>
|
||||
</Block>
|
||||
}
|
||||
></Breakpoint>
|
||||
</Block.TextBox>
|
||||
</Block.Container>
|
||||
</Block>
|
||||
<Breakpoint
|
||||
wide={
|
||||
<Block small>
|
||||
<Block.QuoteContainer>
|
||||
<Block.Divider quote />
|
||||
|
||||
<Block.Quote>
|
||||
One place for everything. Accessible to everyone.
|
||||
</Block.Quote>
|
||||
<Block.LinkButton stretch href={'https://backstage.io/'}>
|
||||
Explore Features
|
||||
</Block.LinkButton>
|
||||
</Block.QuoteContainer>
|
||||
</Block>
|
||||
}
|
||||
></Breakpoint>
|
||||
|
||||
<div
|
||||
style={{
|
||||
zIndex: -1,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundImage: `linear-gradient( to bottom, rgb(18, 18, 18), rgba(0, 0, 0, 0) ), url(../img/dot.svg)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = Background;
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const yaml = require('js-yaml');
|
||||
const React = require('react');
|
||||
const Components = require(`${process.cwd()}/core/Components.js`);
|
||||
const {
|
||||
Block: { Container },
|
||||
BulletLine,
|
||||
} = Components;
|
||||
|
||||
const pluginsDirectory = require('path').join(process.cwd(), 'data/plugins');
|
||||
const pluginMetadata = fs
|
||||
.readdirSync(pluginsDirectory)
|
||||
.map(file =>
|
||||
yaml.safeLoad(fs.readFileSync(`./data/plugins/${file}`, 'utf8')),
|
||||
);
|
||||
const truncate = text =>
|
||||
text.length > 170 ? text.substr(0, 170) + '...' : text;
|
||||
|
||||
const addPluginDocsLink = '/docs/plugins/add-to-marketplace';
|
||||
const defaultIconUrl = 'img/logo-gradient-on-dark.svg';
|
||||
|
||||
const Plugins = () => (
|
||||
<main className="MainContent">
|
||||
<div className="PluginPageLayout">
|
||||
<div className="PluginPageHeader">
|
||||
<h2>Plugins</h2>
|
||||
<span>
|
||||
<a
|
||||
className="PluginAddNewButton ButtonFilled"
|
||||
href={addPluginDocsLink}
|
||||
>
|
||||
<b>Add Plugin</b>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<BulletLine style={{ width: '100% ' }} />
|
||||
<Container wrapped className="grid">
|
||||
{pluginMetadata.map(
|
||||
({
|
||||
iconUrl,
|
||||
title,
|
||||
description,
|
||||
author,
|
||||
authorUrl,
|
||||
documentation,
|
||||
category,
|
||||
}) => (
|
||||
<div className="PluginCard">
|
||||
<div className="PluginCardHeader">
|
||||
<img src={iconUrl || defaultIconUrl} alt={title} />
|
||||
<h2 className="PluginCardTitle">{title}</h2>
|
||||
<p className="Author">
|
||||
by <a href={authorUrl}>{author}</a>
|
||||
</p>
|
||||
<span className="ChipOutlined">{category}</span>
|
||||
</div>
|
||||
<div className="PluginCardBody">
|
||||
<p>{truncate(description)}</p>
|
||||
</div>
|
||||
<Container className="PluginCardFooter">
|
||||
<span>
|
||||
<a
|
||||
className="PluginCardLink ButtonFilled"
|
||||
href={documentation}
|
||||
>
|
||||
docs
|
||||
</a>
|
||||
</span>
|
||||
</Container>
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</Container>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
module.exports = Plugins;
|
||||
@@ -6,6 +6,7 @@
|
||||
"overview/architecture-terminology",
|
||||
"overview/roadmap",
|
||||
"overview/vision",
|
||||
"overview/background",
|
||||
"overview/adopting"
|
||||
],
|
||||
"Getting Started": [
|
||||
@@ -38,7 +39,6 @@
|
||||
"features/software-catalog/software-catalog-overview",
|
||||
"features/software-catalog/system-model",
|
||||
"features/software-catalog/descriptor-format",
|
||||
"features/software-catalog/populating-catalog",
|
||||
"features/software-catalog/extending-the-model",
|
||||
"features/software-catalog/external-integrations",
|
||||
"features/software-catalog/software-catalog-api"
|
||||
@@ -91,7 +91,7 @@
|
||||
{
|
||||
"type": "subcategory",
|
||||
"label": "Publishing",
|
||||
"ids": ["plugins/publishing", "plugins/publish-private"]
|
||||
"ids": ["plugins/publishing", "plugins/publish-private", "plugins/add-to-marketplace"]
|
||||
}
|
||||
],
|
||||
"Configuration": [
|
||||
@@ -132,7 +132,7 @@
|
||||
"ids": ["api/backend"]
|
||||
}
|
||||
],
|
||||
"Tutorials": ["tutorials/index"],
|
||||
"Tutorials": ["tutorials/journey"],
|
||||
"Architecture Decision Records (ADRs)": [
|
||||
"architecture-decisions/adrs-overview",
|
||||
"architecture-decisions/adrs-adr001",
|
||||
|
||||
@@ -17,17 +17,12 @@ const siteConfig = {
|
||||
url: 'https://backstage.io', // Your website URL
|
||||
cname: 'backstage.io',
|
||||
baseUrl: '/', // Base URL for your project */
|
||||
// For github.io type URLs, you would set the url and baseUrl like:
|
||||
// url: 'https://facebook.github.io',
|
||||
// baseUrl: '/test-site/',
|
||||
editUrl: 'https://github.com/spotify/backstage/edit/master/docs/',
|
||||
|
||||
// Used for publishing and more
|
||||
projectName: 'backstage',
|
||||
organizationName: 'Spotify',
|
||||
fossWebsite: 'https://spotify.github.io/',
|
||||
// For top-level user or org sites, the organization is still the same.
|
||||
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
|
||||
// organizationName: 'JoelMarcey'
|
||||
|
||||
// Google Analytics
|
||||
gaTrackingId: 'UA-48912878-10',
|
||||
@@ -43,6 +38,10 @@ const siteConfig = {
|
||||
href: '/docs',
|
||||
label: 'Docs',
|
||||
},
|
||||
{
|
||||
page: 'plugins',
|
||||
label: 'Plugins',
|
||||
},
|
||||
{
|
||||
page: 'blog',
|
||||
blog: true,
|
||||
@@ -52,10 +51,6 @@ const siteConfig = {
|
||||
page: 'demos',
|
||||
label: 'Demos',
|
||||
},
|
||||
{
|
||||
page: 'background',
|
||||
label: 'The Spotify story',
|
||||
},
|
||||
{
|
||||
href: 'https://mailchi.mp/spotify/backstage-community',
|
||||
label: 'Newsletter',
|
||||
@@ -119,6 +114,12 @@ const siteConfig = {
|
||||
stylesheets: [
|
||||
'https://fonts.googleapis.com/css?family=IBM+Plex+Mono:500,700&display=swap',
|
||||
],
|
||||
|
||||
algolia: {
|
||||
apiKey: '8d115c9875ba0f4feaee95bab55a1645',
|
||||
indexName: 'backstage',
|
||||
searchParameters: {}, // Optional (if provided by Algolia)
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = siteConfig;
|
||||
|
||||
@@ -58,7 +58,7 @@ ol,
|
||||
li,
|
||||
td {
|
||||
font-family: Helvetica Neue, sans-serif;
|
||||
color: $textColor;
|
||||
color: #e4e4e4;
|
||||
}
|
||||
|
||||
.mainWrapper {
|
||||
@@ -216,7 +216,7 @@ td {
|
||||
code {
|
||||
font-family: IBM Plex Mono, Menlo, Monaco, Consolas, Courier New, monospace;
|
||||
font-weight: 500;
|
||||
background-color: #0e0e0e;
|
||||
background-color: #272822;
|
||||
}
|
||||
|
||||
/* .stripe {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
.PluginCard {
|
||||
background-color: #272822;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 1rem;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-auto-rows: 1fr;
|
||||
padding-top: 32px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 815px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.PluginCard img {
|
||||
float: left;
|
||||
margin: 0px 16px 8px 0px;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.PluginCardHeader {
|
||||
max-height: fit-content;
|
||||
min-height: fit-content;
|
||||
}
|
||||
|
||||
.PluginCardTitle {
|
||||
color: white;
|
||||
vertical-align: top;
|
||||
margin: 8px 0px 0px 16px;
|
||||
}
|
||||
|
||||
.PluginAddNewButton {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.ButtonFilled {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #36BAA2;
|
||||
color: white;
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.ButtonFilled:hover {
|
||||
border: 1px solid #36BAA2;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.ChipOutlined {
|
||||
font-size: small;
|
||||
border-radius: 16px;
|
||||
padding: 2px 8px;
|
||||
border: 1px solid #36BAA2;
|
||||
color: #36BAA2;
|
||||
}
|
||||
|
||||
.PluginCardLink {
|
||||
padding: 2px 8px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.PluginPageLayout {
|
||||
margin: auto;
|
||||
max-width: 1430px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.PluginPageHeader {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.PluginPageHeader h2 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.PluginCardBody {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.PluginCardFooter {
|
||||
position: relative;
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
.Author, .Author a {
|
||||
margin-bottom: 0.25em;
|
||||
color: rgba(255,255,255, 0.6);
|
||||
}
|
||||
|
||||
.Author a:hover {
|
||||
color: white;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ nav:
|
||||
- Architecture and terminology: 'overview/architecture-terminology.md'
|
||||
- Roadmap: 'overview/roadmap.md'
|
||||
- Vision: 'overview/vision.md'
|
||||
- The Spotify story: 'overview/background.md'
|
||||
- Strategies for adopting: 'overview/adopting.md'
|
||||
- Getting started:
|
||||
- Running Backstage locally: 'getting-started/index.md'
|
||||
@@ -27,6 +28,7 @@ nav:
|
||||
- Overview: 'features/software-catalog/index.md'
|
||||
- System model: 'features/software-catalog/system-model.md'
|
||||
- YAML File Format: 'features/software-catalog/descriptor-format.md'
|
||||
- Configuration: 'features/software-catalog/configuration.md'
|
||||
- Extending the model: 'features/software-catalog/extending-the-model.md'
|
||||
- External integrations: 'features/software-catalog/external-integrations.md'
|
||||
- API: 'features/software-catalog/api.md'
|
||||
|
||||
@@ -60,5 +60,10 @@
|
||||
"*.{json,md}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"transformModules": [
|
||||
"@kyma-project/asyncapi-react"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.19",
|
||||
"@backstage/core": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-api-docs": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-circleci": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-explore": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-github-actions": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-graphiql": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-jenkins": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-newrelic": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-register-component": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-rollbar": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-tech-radar": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.19",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.19",
|
||||
"@backstage/theme": "^0.1.1-alpha.19",
|
||||
"@backstage/cli": "^0.1.1-alpha.20",
|
||||
"@backstage/core": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-api-docs": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-circleci": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-explore": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-github-actions": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-graphiql": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-jenkins": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-newrelic": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-register-component": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-rollbar": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-tech-radar": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.20",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.20",
|
||||
"@backstage/theme": "^0.1.1-alpha.20",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
|
||||
@@ -27,6 +27,9 @@ describe('App', () => {
|
||||
data: {
|
||||
app: { title: 'Test' },
|
||||
backend: { baseUrl: 'http://localhost:7000' },
|
||||
lighthouse: {
|
||||
baseUrl: 'http://localhost:3003',
|
||||
},
|
||||
techdocs: {
|
||||
storageUrl: 'http://localhost:7000/techdocs/static/docs',
|
||||
},
|
||||
|
||||
@@ -24,6 +24,8 @@ import {
|
||||
ErrorAlerter,
|
||||
featureFlagsApiRef,
|
||||
FeatureFlags,
|
||||
discoveryApiRef,
|
||||
UrlPatternDiscovery,
|
||||
GoogleAuth,
|
||||
GithubAuth,
|
||||
OAuth2,
|
||||
@@ -90,6 +92,10 @@ export const apis = (config: ConfigApi) => {
|
||||
|
||||
const builder = ApiRegistry.builder();
|
||||
|
||||
const discoveryApi = builder.add(
|
||||
discoveryApiRef,
|
||||
UrlPatternDiscovery.compile(`${backendUrl}/{{ pluginId }}`),
|
||||
);
|
||||
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
|
||||
const errorApi = builder.add(
|
||||
errorApiRef,
|
||||
@@ -108,7 +114,7 @@ export const apis = (config: ConfigApi) => {
|
||||
|
||||
builder.add(featureFlagsApiRef, new FeatureFlags());
|
||||
|
||||
builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003'));
|
||||
builder.add(lighthouseApiRef, LighthouseRestApi.fromConfig(config));
|
||||
|
||||
builder.add(travisCIApiRef, new TravisCIApi());
|
||||
builder.add(githubPullRequestsApiRef, new GithubPullRequestsClient());
|
||||
@@ -121,8 +127,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
googleAuthApiRef,
|
||||
GoogleAuth.create({
|
||||
backendUrl,
|
||||
basePath: '/auth/',
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
@@ -130,8 +135,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
microsoftAuthApiRef,
|
||||
MicrosoftAuth.create({
|
||||
backendUrl,
|
||||
basePath: '/auth/',
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
@@ -139,8 +143,7 @@ export const apis = (config: ConfigApi) => {
|
||||
const githubAuthApi = builder.add(
|
||||
githubAuthApiRef,
|
||||
GithubAuth.create({
|
||||
backendUrl,
|
||||
basePath: '/auth/',
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
@@ -148,8 +151,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
oktaAuthApiRef,
|
||||
OktaAuth.create({
|
||||
backendUrl,
|
||||
basePath: '/auth/',
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
@@ -157,8 +159,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
gitlabAuthApiRef,
|
||||
GitlabAuth.create({
|
||||
backendUrl,
|
||||
basePath: '/auth/',
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
@@ -166,8 +167,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
auth0AuthApiRef,
|
||||
Auth0Auth.create({
|
||||
backendUrl,
|
||||
basePath: '/auth/',
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
@@ -175,8 +175,7 @@ export const apis = (config: ConfigApi) => {
|
||||
builder.add(
|
||||
oauth2ApiRef,
|
||||
OAuth2.create({
|
||||
backendUrl,
|
||||
basePath: '/auth/',
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
}),
|
||||
);
|
||||
@@ -189,21 +188,9 @@ export const apis = (config: ConfigApi) => {
|
||||
}),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
catalogApiRef,
|
||||
new CatalogClient({
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/catalog',
|
||||
}),
|
||||
);
|
||||
builder.add(catalogApiRef, new CatalogClient({ discoveryApi }));
|
||||
|
||||
builder.add(
|
||||
scaffolderApiRef,
|
||||
new ScaffolderApi({
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/scaffolder/v1',
|
||||
}),
|
||||
);
|
||||
builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi }));
|
||||
|
||||
builder.add(gitOpsApiRef, new GitOpsRestApi('http://localhost:3008'));
|
||||
|
||||
@@ -224,13 +211,7 @@ export const apis = (config: ConfigApi) => {
|
||||
]),
|
||||
);
|
||||
|
||||
builder.add(
|
||||
rollbarApiRef,
|
||||
new RollbarClient({
|
||||
apiOrigin: backendUrl,
|
||||
basePath: '/rollbar',
|
||||
}),
|
||||
);
|
||||
builder.add(rollbarApiRef, new RollbarClient({ discoveryApi }));
|
||||
|
||||
builder.add(
|
||||
techdocsStorageApiRef,
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
SidebarUserSettings,
|
||||
SidebarThemeToggle,
|
||||
SidebarPinButton,
|
||||
DefaultProviderSettings,
|
||||
} from '@backstage/core';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { graphiQLRouteRef } from '@backstage/plugin-graphiql';
|
||||
@@ -107,7 +108,7 @@ const Root: FC<{}> = ({ children }) => (
|
||||
<SidebarSpace />
|
||||
<SidebarDivider />
|
||||
<SidebarThemeToggle />
|
||||
<SidebarUserSettings />
|
||||
<SidebarUserSettings providerSettings={<DefaultProviderSettings />} />
|
||||
<SidebarPinButton />
|
||||
</Sidebar>
|
||||
{children}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/backend-common",
|
||||
"description": "Common functionality library for Backstage backends",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -29,22 +29,24 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "^0.1.1-alpha.19",
|
||||
"@backstage/config": "^0.1.1-alpha.19",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.19",
|
||||
"@backstage/cli-common": "^0.1.1-alpha.20",
|
||||
"@backstage/config": "^0.1.1-alpha.20",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.20",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.6",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"express-prom-bundle": "^6.1.0",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"helmet": "^4.0.0",
|
||||
"knex": "^0.21.1",
|
||||
"lodash": "^4.17.15",
|
||||
"morgan": "^1.10.0",
|
||||
"prom-client": "^12.0.0",
|
||||
"selfsigned": "^1.10.7",
|
||||
"stoppable": "^1.1.0",
|
||||
"winston": "^3.2.1",
|
||||
"selfsigned": "^1.10.7"
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-connection-string": "^2.3.0"
|
||||
@@ -55,7 +57,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.19",
|
||||
"@backstage/cli": "^0.1.1-alpha.20",
|
||||
"@types/compression": "^1.7.0",
|
||||
"@types/http-errors": "^1.6.3",
|
||||
"@types/morgan": "^1.9.0",
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
HttpsSettings,
|
||||
} from './config';
|
||||
import { createHttpServer, createHttpsServer } from './hostFactory';
|
||||
import { metricsHandler } from './metrics';
|
||||
|
||||
const DEFAULT_PORT = 7000;
|
||||
// '' is express default, which listens to all interfaces
|
||||
@@ -48,6 +49,7 @@ export class ServiceBuilderImpl implements ServiceBuilder {
|
||||
private logger: Logger | undefined;
|
||||
private corsOptions: cors.CorsOptions | undefined;
|
||||
private httpsSettings: HttpsSettings | undefined;
|
||||
private enableMetrics: boolean = true;
|
||||
private routers: [string, Router][];
|
||||
// Reference to the module where builder is created - needed for hot module
|
||||
// reloading
|
||||
@@ -82,6 +84,9 @@ export class ServiceBuilderImpl implements ServiceBuilder {
|
||||
this.httpsSettings = httpsSettings;
|
||||
}
|
||||
|
||||
// For now, configuration of metrics is a simple boolean and active by default
|
||||
this.enableMetrics = backendConfig.getOptionalBoolean('metrics') !== false;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -131,6 +136,9 @@ export class ServiceBuilderImpl implements ServiceBuilder {
|
||||
}
|
||||
app.use(compression());
|
||||
app.use(express.json());
|
||||
if (this.enableMetrics) {
|
||||
app.use(metricsHandler());
|
||||
}
|
||||
app.use(requestLoggingHandler());
|
||||
for (const [root, route] of this.routers) {
|
||||
app.use(root, route);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 prom from 'prom-client';
|
||||
import promBundle from 'express-prom-bundle';
|
||||
import { RequestHandler } from 'express';
|
||||
|
||||
/**
|
||||
* Adds a /metrics endpoint, register default runtime metrics and instrument the router.
|
||||
*/
|
||||
export function metricsHandler(): RequestHandler {
|
||||
// We can only initialize the metrics once and have to clean them up between hot reloads
|
||||
prom.register.clear();
|
||||
|
||||
return promBundle({
|
||||
includeMethod: true,
|
||||
includePath: true,
|
||||
// Using includePath alone is problematic, as it will include path labels with high
|
||||
// cardinality (e.g. path params). Instead we would have to template them. However, this
|
||||
// is difficult, as every backend plugin might use different routes. Instead we only take
|
||||
// the first directory of the path, to have at least an idea how each plugin performs:
|
||||
normalizePath: [['^/([^/]*)/.*', '/$1']],
|
||||
promClient: { collectDefaultMetrics: {} },
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"private": true,
|
||||
@@ -18,18 +18,20 @@
|
||||
"migrate:create": "knex migrate:make -x ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.19",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.19",
|
||||
"@backstage/config": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-auth-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-identity-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.19",
|
||||
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.19",
|
||||
"example-app": "^0.1.1-alpha.20",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.20",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.20",
|
||||
"@backstage/config": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-app-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-auth-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-identity-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.20",
|
||||
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.20",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"dockerode": "^3.2.0",
|
||||
"express": "^4.17.1",
|
||||
@@ -40,7 +42,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.19",
|
||||
"@backstage/cli": "^0.1.1-alpha.20",
|
||||
"@types/dockerode": "^2.5.32",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
|
||||
@@ -40,6 +40,7 @@ import sentry from './plugins/sentry';
|
||||
import proxy from './plugins/proxy';
|
||||
import techdocs from './plugins/techdocs';
|
||||
import graphql from './plugins/graphql';
|
||||
import app from './plugins/app';
|
||||
import { PluginEnvironment } from './types';
|
||||
|
||||
function makeCreateEnv(loadedConfigs: AppConfig[]) {
|
||||
@@ -71,6 +72,7 @@ async function main() {
|
||||
const sentryEnv = useHotMemoize(module, () => createEnv('sentry'));
|
||||
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
|
||||
const graphqlEnv = useHotMemoize(module, () => createEnv('graphql'));
|
||||
const appEnv = useHotMemoize(module, () => createEnv('app'));
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
.loadConfig(configReader)
|
||||
@@ -82,8 +84,9 @@ async function main() {
|
||||
.addRouter('/auth', await auth(authEnv))
|
||||
.addRouter('/identity', await identity(identityEnv))
|
||||
.addRouter('/techdocs', await techdocs(techdocsEnv))
|
||||
.addRouter('/proxy', await proxy(proxyEnv))
|
||||
.addRouter('/graphql', await graphql(graphqlEnv));
|
||||
.addRouter('/proxy', await proxy(proxyEnv, '/proxy'))
|
||||
.addRouter('/graphql', await graphql(graphqlEnv))
|
||||
.addRouter('', await app(appEnv));
|
||||
|
||||
await service.start().catch(err => {
|
||||
console.log(err);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 { createRouter } from '@backstage/plugin-app-backend';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({ logger }: PluginEnvironment) {
|
||||
return await createRouter({
|
||||
logger,
|
||||
appPackageName: 'example-app',
|
||||
});
|
||||
}
|
||||
@@ -13,13 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import { createRouter } from '@backstage/plugin-proxy-backend';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
config,
|
||||
}: PluginEnvironment) {
|
||||
return await createRouter({ logger, config });
|
||||
export default async function createPlugin(
|
||||
{ logger, config }: PluginEnvironment,
|
||||
pathPrefix: string,
|
||||
) {
|
||||
return await createRouter({ logger, config, pathPrefix });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/catalog-model",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,7 +20,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.19",
|
||||
"@backstage/config": "^0.1.1-alpha.20",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"@types/yup": "^0.28.2",
|
||||
"json-schema": "^0.2.5",
|
||||
@@ -29,7 +29,7 @@
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.19",
|
||||
"@backstage/cli": "^0.1.1-alpha.20",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/lodash": "^4.14.151",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli-common",
|
||||
"description": "Common functionality used by cli, backend, and create-app",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"private": false,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -28,6 +28,9 @@ module.exports = {
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
globals: {
|
||||
__non_webpack_require__: 'readonly',
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
|
||||
@@ -25,43 +25,6 @@ async function getConfig() {
|
||||
return require(path.resolve('jest.config.ts'));
|
||||
}
|
||||
|
||||
const options = {
|
||||
rootDir: path.resolve('src'),
|
||||
coverageDirectory: path.resolve('coverage'),
|
||||
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'],
|
||||
moduleNameMapper: {
|
||||
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
|
||||
},
|
||||
|
||||
// We build .esm.js files with plugin:build, so to be able to load these in tests they need to be transformed
|
||||
// TODO: jest is working on module support, it's possible that we can remove this in the future
|
||||
transform: {
|
||||
'\\.esm\\.js$': require.resolve('jest-esm-transformer'),
|
||||
'\\.(js|jsx|ts|tsx)$': [
|
||||
require.resolve('ts-jest'),
|
||||
{ isolatedModules: true },
|
||||
],
|
||||
'\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve(
|
||||
'./jestFileTransform.js',
|
||||
),
|
||||
},
|
||||
|
||||
// A bit more opinionated
|
||||
testMatch: ['**/?(*.)test.{js,jsx,mjs,ts,tsx}'],
|
||||
|
||||
// Default behaviour is to not apply transforms for node_modules, but we still want
|
||||
// to apply the esm-transformer to .esm.js files, since that's what we use in backstage packages.
|
||||
// The @kyma-project/asyncapi-react library needs to be transformed.
|
||||
transformIgnorePatterns: [
|
||||
'/node_modules/(?!@kyma-project/asyncapi-react/)(?!.*\\.(?:esm\\.js|bmp|gif|jpg|jpeg|png|frag|xml|svg)$)',
|
||||
],
|
||||
};
|
||||
|
||||
// Use src/setupTests.ts as the default location for configuring test env
|
||||
if (fs.existsSync('src/setupTests.ts')) {
|
||||
options.setupFilesAfterEnv = ['<rootDir>/setupTests.ts'];
|
||||
}
|
||||
|
||||
// We read all "jest" config fields in package.json files all the way to the filesystem root.
|
||||
// All configs are merged together to create the final config, with longer paths taking precedence.
|
||||
// The merging of the configs is shallow, meaning e.g. all transforms are replaced if new ones are defined.
|
||||
@@ -92,6 +55,56 @@ async function getConfig() {
|
||||
currentPath = newPath;
|
||||
}
|
||||
|
||||
// We add an additional Jest config parameter only known by the Backstage CLI
|
||||
// called `transformModules`. It's a list of modules that we want to apply
|
||||
// our configured jest transformations for.
|
||||
// This is useful when packages are published in untranspiled ESM or TS form.
|
||||
const transformModules = pkgJsonConfigs
|
||||
.flatMap(conf => {
|
||||
const modules = conf.transformModules || [];
|
||||
delete conf.transformModules;
|
||||
return modules;
|
||||
})
|
||||
.map(name => `${name}/`)
|
||||
.join('|');
|
||||
const transformModulePattern = transformModules && `(?!${transformModules})`;
|
||||
|
||||
const options = {
|
||||
rootDir: path.resolve('src'),
|
||||
coverageDirectory: path.resolve('coverage'),
|
||||
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'],
|
||||
moduleNameMapper: {
|
||||
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
|
||||
},
|
||||
|
||||
// We build .esm.js files with plugin:build, so to be able to load these in tests they need to be transformed
|
||||
// TODO: jest is working on module support, it's possible that we can remove this in the future
|
||||
transform: {
|
||||
'\\.esm\\.js$': require.resolve('jest-esm-transformer'),
|
||||
'\\.(js|jsx|ts|tsx)$': [
|
||||
require.resolve('ts-jest'),
|
||||
{ isolatedModules: true },
|
||||
],
|
||||
'\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)$': require.resolve(
|
||||
'./jestFileTransform.js',
|
||||
),
|
||||
},
|
||||
|
||||
// A bit more opinionated
|
||||
testMatch: ['**/?(*.)test.{js,jsx,mjs,ts,tsx}'],
|
||||
|
||||
// Default behaviour is to not apply transforms for node_modules, but we still want
|
||||
// to apply the esm-transformer to .esm.js files, since that's what we use in backstage packages.
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/${transformModulePattern}(?:(?!\\.esm).)*\\.(?:js|json)$`,
|
||||
],
|
||||
};
|
||||
|
||||
// Use src/setupTests.ts as the default location for configuring test env
|
||||
if (fs.existsSync('src/setupTests.ts')) {
|
||||
options.setupFilesAfterEnv = ['<rootDir>/setupTests.ts'];
|
||||
}
|
||||
|
||||
return Object.assign(options, ...pkgJsonConfigs);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -28,9 +28,9 @@
|
||||
"backstage-cli": "bin/backstage-cli"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "^0.1.1-alpha.19",
|
||||
"@backstage/config": "^0.1.1-alpha.19",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.19",
|
||||
"@backstage/cli-common": "^0.1.1-alpha.20",
|
||||
"@backstage/config": "^0.1.1-alpha.20",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.20",
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@lerna/package-graph": "^3.18.5",
|
||||
"@lerna/project": "^3.18.0",
|
||||
@@ -40,17 +40,17 @@
|
||||
"@rollup/plugin-yaml": "^2.1.1",
|
||||
"@spotify/eslint-config": "^7.0.1",
|
||||
"@sucrase/webpack-loader": "^2.0.0",
|
||||
"@svgr/plugin-jsx": "4.3.x",
|
||||
"@svgr/plugin-jsx": "5.4.x",
|
||||
"@svgr/plugin-svgo": "4.3.x",
|
||||
"@svgr/rollup": "5.4.x",
|
||||
"@svgr/webpack": "4.3.x",
|
||||
"@svgr/webpack": "5.4.x",
|
||||
"@types/start-server-webpack-plugin": "^2.2.0",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@types/webpack-node-externals": "^2.5.0",
|
||||
"bfj": "^7.0.2",
|
||||
"chalk": "^4.0.0",
|
||||
"chokidar": "^3.3.1",
|
||||
"commander": "^4.1.1",
|
||||
"commander": "^6.1.0",
|
||||
"css-loader": "^3.5.3",
|
||||
"dashify": "^2.0.0",
|
||||
"diff": "^4.0.2",
|
||||
|
||||
@@ -88,15 +88,25 @@ export const makeConfigs = async (
|
||||
}),
|
||||
resolve({ mainFields }),
|
||||
commonjs({
|
||||
include: ['node_modules/**', '../../node_modules/**'],
|
||||
exclude: ['**/*.stories.*', '**/*.test.*'],
|
||||
include: /node_modules/,
|
||||
exclude: [/\/[^/]+\.(?:stories|test)\.[^/]+$/],
|
||||
}),
|
||||
postcss(),
|
||||
imageFiles({ exclude: '**/*.icon.svg' }),
|
||||
imageFiles({
|
||||
exclude: /\.icon\.svg$/,
|
||||
include: [
|
||||
/\.css$/,
|
||||
/\.svg$/,
|
||||
/\.png$/,
|
||||
/\.gif$/,
|
||||
/\.jpg$/,
|
||||
/\.jpeg$/,
|
||||
],
|
||||
}),
|
||||
json(),
|
||||
yaml(),
|
||||
svgr({
|
||||
include: '**/*.icon.svg',
|
||||
include: /\.icon\.svg$/,
|
||||
template: svgrTemplate,
|
||||
}),
|
||||
esbuild({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/config-loader",
|
||||
"description": "Config loading functionality used by Backstage backend, and CLI",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,7 +30,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.19",
|
||||
"@backstage/config": "^0.1.1-alpha.20",
|
||||
"fs-extra": "^9.0.0",
|
||||
"yaml": "^1.9.2",
|
||||
"yup": "^0.29.1"
|
||||
|
||||
@@ -14,5 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { readEnvConfig } from './lib';
|
||||
export { loadConfig } from './loader';
|
||||
export type { LoadConfigOptions } from './loader';
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { readEnv } from './env';
|
||||
import { readEnvConfig } from './env';
|
||||
|
||||
describe('readEnv', () => {
|
||||
describe('readEnvConfig', () => {
|
||||
it('should return empty config for empty env', () => {
|
||||
expect(readEnv({})).toEqual([]);
|
||||
expect(readEnvConfig({})).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return empty config for no matching keys', () => {
|
||||
expect(
|
||||
readEnv({
|
||||
readEnvConfig({
|
||||
NODE_ENV: 'production',
|
||||
NOPE_ENV: 'development',
|
||||
APP_CONFIG: 'foo',
|
||||
@@ -34,7 +34,7 @@ describe('readEnv', () => {
|
||||
|
||||
it('should create config from env', () => {
|
||||
expect(
|
||||
readEnv({
|
||||
readEnvConfig({
|
||||
NODE_ENV: 'production',
|
||||
APP_CONFIG_foo: '"bar"',
|
||||
APP_CONFIG_numbers_a: '1',
|
||||
@@ -57,22 +57,22 @@ describe('readEnv', () => {
|
||||
});
|
||||
|
||||
it('should accept string values', () => {
|
||||
expect(readEnv({ APP_CONFIG_foo: '"abc"', APP_CONFIG_bar: 'xyz' })).toEqual(
|
||||
[
|
||||
{
|
||||
data: {
|
||||
foo: 'abc',
|
||||
bar: 'xyz',
|
||||
},
|
||||
context: 'env',
|
||||
expect(
|
||||
readEnvConfig({ APP_CONFIG_foo: '"abc"', APP_CONFIG_bar: 'xyz' }),
|
||||
).toEqual([
|
||||
{
|
||||
data: {
|
||||
foo: 'abc',
|
||||
bar: 'xyz',
|
||||
},
|
||||
],
|
||||
);
|
||||
context: 'env',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should accept complex objects', () => {
|
||||
expect(
|
||||
readEnv({
|
||||
readEnvConfig({
|
||||
APP_CONFIG_foo: '{ "a": 123, "b": "123", "c": [] }',
|
||||
APP_CONFIG_bar: '[123, "abc", {}]',
|
||||
}),
|
||||
@@ -95,7 +95,7 @@ describe('readEnv', () => {
|
||||
['APP_CONFIG_fo o'],
|
||||
['APP_CONFIG_foo_(foo)_foo'],
|
||||
])('should reject invalid key %p', key => {
|
||||
expect(() => readEnv({ [key]: '0' })).toThrow(
|
||||
expect(() => readEnvConfig({ [key]: '0' })).toThrow(
|
||||
`Invalid env config key '${key.replace('APP_CONFIG_', '')}'`,
|
||||
);
|
||||
});
|
||||
@@ -103,7 +103,7 @@ describe('readEnv', () => {
|
||||
it.each([['hello'], ['"hello'], ['{'], ['}']])(
|
||||
'should fallback to string when invalid json value %p',
|
||||
value => {
|
||||
expect(readEnv({ APP_CONFIG_foo: value })).toEqual([
|
||||
expect(readEnvConfig({ APP_CONFIG_foo: value })).toEqual([
|
||||
{
|
||||
data: {
|
||||
foo: value,
|
||||
@@ -116,7 +116,7 @@ describe('readEnv', () => {
|
||||
|
||||
it('should not allow null as a value', () => {
|
||||
expect(() =>
|
||||
readEnv({
|
||||
readEnvConfig({
|
||||
APP_CONFIG_foo: 'null',
|
||||
}),
|
||||
).toThrow(
|
||||
@@ -126,7 +126,7 @@ describe('readEnv', () => {
|
||||
|
||||
it('should not allow duplicate values', () => {
|
||||
expect(() =>
|
||||
readEnv({
|
||||
readEnvConfig({
|
||||
APP_CONFIG_foo_bar: '1',
|
||||
APP_CONFIG_foo_bar_baz: '2',
|
||||
}),
|
||||
@@ -137,7 +137,7 @@ describe('readEnv', () => {
|
||||
|
||||
it('should not allow mixing of objects and other values', () => {
|
||||
expect(() =>
|
||||
readEnv({
|
||||
readEnvConfig({
|
||||
APP_CONFIG_nested_foo: '1',
|
||||
APP_CONFIG_nested: '2',
|
||||
}),
|
||||
|
||||
@@ -39,7 +39,7 @@ const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i;
|
||||
*
|
||||
* APP_CONFIG_app_title='"My Title"'
|
||||
*/
|
||||
export function readEnv(env: {
|
||||
export function readEnvConfig(env: {
|
||||
[name: string]: string | undefined;
|
||||
}): AppConfig[] {
|
||||
let data: JsonObject | undefined = undefined;
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
|
||||
export { resolveStaticConfig } from './resolver';
|
||||
export { readConfigFile } from './reader';
|
||||
export { readEnv } from './env';
|
||||
export { readEnvConfig } from './env';
|
||||
export { readSecret } from './secrets';
|
||||
|
||||
@@ -20,7 +20,7 @@ import { AppConfig, JsonObject } from '@backstage/config';
|
||||
import {
|
||||
resolveStaticConfig,
|
||||
readConfigFile,
|
||||
readEnv,
|
||||
readEnvConfig,
|
||||
readSecret,
|
||||
} from './lib';
|
||||
|
||||
@@ -102,7 +102,7 @@ export async function loadConfig(
|
||||
);
|
||||
}
|
||||
|
||||
configs.push(...readEnv(process.env));
|
||||
configs.push(...readEnvConfig(process.env));
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/config",
|
||||
"description": "Config API used by Backstage core, backend, and CLI",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-api",
|
||||
"description": "Internal Core API used by Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.19",
|
||||
"version": "0.1.1-alpha.20",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,8 +29,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.19",
|
||||
"@backstage/theme": "^0.1.1-alpha.19",
|
||||
"@backstage/config": "^0.1.1-alpha.20",
|
||||
"@backstage/theme": "^0.1.1-alpha.20",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@types/react": "^16.9",
|
||||
@@ -41,8 +41,8 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.19",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.19",
|
||||
"@backstage/cli": "^0.1.1-alpha.20",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.20",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { createApiRef } from '../ApiRef';
|
||||
|
||||
/**
|
||||
* The discovery API is used to provide a mechanism for plugins to
|
||||
* discover the endpoint to use to talk to their backend counterpart.
|
||||
*
|
||||
* The purpose of the discovery API is to allow for many different deployment
|
||||
* setups and routing methods through a central configuration, instead
|
||||
* of letting each individual plugin manage that configuration.
|
||||
*
|
||||
* Implementations of the discovery API can be a simple as a URL pattern
|
||||
* using the pluginId, but could also have overrides for individual plugins,
|
||||
* or query a separate discovery service.
|
||||
*/
|
||||
export type DiscoveryApi = {
|
||||
/**
|
||||
* Returns the HTTP base backend URL for a given plugin, without a trailing slash.
|
||||
*
|
||||
* This method must always be called just before making a request. as opposed to
|
||||
* fetching the URL when constructing an API client. That is to ensure that more
|
||||
* flexible routing patterns can be supported.
|
||||
*
|
||||
* For example, asking for the URL for `auth` may return something
|
||||
* like `https://backstage.example.com/api/auth`
|
||||
*/
|
||||
getBaseUrl(pluginId: string): Promise<string>;
|
||||
};
|
||||
|
||||
export const discoveryApiRef = createApiRef<DiscoveryApi>({
|
||||
id: 'core.discovery',
|
||||
description: 'Provides service discovery of backend plugins',
|
||||
});
|
||||
@@ -27,6 +27,7 @@ export * from './AppThemeApi';
|
||||
export * from './ConfigApi';
|
||||
export * from './ErrorApi';
|
||||
export * from './FeatureFlagsApi';
|
||||
export * from './DiscoveryApi';
|
||||
export * from './IdentityApi';
|
||||
export * from './OAuthRequestApi';
|
||||
export * from './StorageApi';
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 { UrlPatternDiscovery } from './UrlPatternDiscovery';
|
||||
|
||||
describe('UrlPatternDiscovery', () => {
|
||||
it('should not require interpolation', async () => {
|
||||
const discoveryApi = UrlPatternDiscovery.compile('http://example.com');
|
||||
await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe(
|
||||
'http://example.com',
|
||||
);
|
||||
});
|
||||
|
||||
it('should use a plain pattern', async () => {
|
||||
const discoveryApi = UrlPatternDiscovery.compile(
|
||||
'http://localhost:7000/{{ pluginId }}',
|
||||
);
|
||||
await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe(
|
||||
'http://localhost:7000/my-plugin',
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow for multiple interpolation points', async () => {
|
||||
const discoveryApi = UrlPatternDiscovery.compile(
|
||||
'https://{{pluginId }}.example.com/api/{{ pluginId}}',
|
||||
);
|
||||
await expect(discoveryApi.getBaseUrl('my-plugin')).resolves.toBe(
|
||||
'https://my-plugin.example.com/api/my-plugin',
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate that the pattern is a valid URL', () => {
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('example.com');
|
||||
}).toThrow('Invalid discovery URL pattern, Invalid URL: example.com');
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('http://');
|
||||
}).toThrow('Invalid discovery URL pattern, Invalid URL: http://');
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('abc123');
|
||||
}).toThrow('Invalid discovery URL pattern, Invalid URL: abc123');
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('http://example.com:{{pluginId}}');
|
||||
}).toThrow(
|
||||
'Invalid discovery URL pattern, Invalid URL: http://example.com:pluginId',
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('/{{pluginId}}');
|
||||
}).toThrow('Invalid discovery URL pattern, Invalid URL: /pluginId');
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('http://localhost/{{pluginId}}?forbidden');
|
||||
}).toThrow('Invalid discovery URL pattern, URL must not have a query');
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('http://localhost/{{pluginId}}#forbidden');
|
||||
}).toThrow('Invalid discovery URL pattern, URL must not have a hash');
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('http://localhost/{{pluginId}}/');
|
||||
}).toThrow('Invalid discovery URL pattern, URL must not end with a slash');
|
||||
|
||||
expect(() => {
|
||||
UrlPatternDiscovery.compile('http://localhost/');
|
||||
}).toThrow('Invalid discovery URL pattern, URL must not end with a slash');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 { DiscoveryApi } from '../../definitions/DiscoveryApi';
|
||||
|
||||
/**
|
||||
* UrlPatternDiscovery is a lightweight DiscoveryApi implementation.
|
||||
* It uses a single template string to construct URLs for each plugin.
|
||||
*/
|
||||
export class UrlPatternDiscovery implements DiscoveryApi {
|
||||
/**
|
||||
* Creates a new UrlPatternDiscovery given a template. The the only
|
||||
* interpolation done for the template is to replace instances of `{{pluginId}}`
|
||||
* with the ID of the plugin being requested.
|
||||
*
|
||||
* Example pattern: `http://localhost:7000/api/{{ pluginId }}`
|
||||
*/
|
||||
static compile(pattern: string): UrlPatternDiscovery {
|
||||
const parts = pattern.split(/\{\{\s*pluginId\s*\}\}/);
|
||||
|
||||
try {
|
||||
const urlStr = parts.join('pluginId');
|
||||
const url = new URL(urlStr);
|
||||
if (url.hash) {
|
||||
throw new Error('URL must not have a hash');
|
||||
}
|
||||
if (url.search) {
|
||||
throw new Error('URL must not have a query');
|
||||
}
|
||||
if (urlStr.endsWith('/')) {
|
||||
throw new Error('URL must not end with a slash');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid discovery URL pattern, ${error.message}`);
|
||||
}
|
||||
|
||||
return new UrlPatternDiscovery(parts);
|
||||
}
|
||||
|
||||
private constructor(private readonly parts: string[]) {}
|
||||
|
||||
async getBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.parts.join(pluginId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This folder contains implementations for all core APIs.
|
||||
//
|
||||
// Plugins should rely on these APIs for functionality as much as possible.
|
||||
|
||||
export { UrlPatternDiscovery } from './UrlPatternDiscovery';
|
||||
@@ -27,16 +27,17 @@ import {
|
||||
AuthRequestOptions,
|
||||
BackstageIdentity,
|
||||
} from '../../../definitions/auth';
|
||||
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
|
||||
import {
|
||||
OAuthRequestApi,
|
||||
AuthProvider,
|
||||
DiscoveryApi,
|
||||
} from '../../../definitions';
|
||||
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
|
||||
import { RefreshingAuthSessionManager } from '../../../../lib/AuthSessionManager';
|
||||
import { Observable } from '../../../../types';
|
||||
|
||||
type CreateOptions = {
|
||||
// TODO(Following the words of Rugvip): These two should be grabbed from global config when available, they're not unique to Auth0Auth
|
||||
backendUrl: string;
|
||||
basePath: string;
|
||||
|
||||
discoveryApi: DiscoveryApi;
|
||||
oauthRequestApi: OAuthRequestApi;
|
||||
|
||||
environment?: string;
|
||||
@@ -67,15 +68,13 @@ class Auth0Auth
|
||||
BackstageIdentityApi,
|
||||
SessionStateApi {
|
||||
static create({
|
||||
backendUrl,
|
||||
basePath,
|
||||
discoveryApi,
|
||||
environment = 'development',
|
||||
provider = DEFAULT_PROVIDER,
|
||||
oauthRequestApi,
|
||||
}: CreateOptions) {
|
||||
const connector = new DefaultAuthConnector({
|
||||
backendUrl,
|
||||
basePath,
|
||||
discoveryApi,
|
||||
environment,
|
||||
provider,
|
||||
oauthRequestApi: oauthRequestApi,
|
||||
|
||||
@@ -25,7 +25,11 @@ import {
|
||||
BackstageIdentity,
|
||||
AuthRequestOptions,
|
||||
} from '../../../definitions/auth';
|
||||
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
|
||||
import {
|
||||
OAuthRequestApi,
|
||||
AuthProvider,
|
||||
DiscoveryApi,
|
||||
} from '../../../definitions';
|
||||
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
|
||||
import {
|
||||
AuthSessionStore,
|
||||
@@ -34,10 +38,7 @@ import {
|
||||
import { Observable } from '../../../../types';
|
||||
|
||||
type CreateOptions = {
|
||||
// TODO(Rugvip): These two should be grabbed from global config when available, they're not unique to GithubAuth
|
||||
backendUrl: string;
|
||||
basePath: string;
|
||||
|
||||
discoveryApi: DiscoveryApi;
|
||||
oauthRequestApi: OAuthRequestApi;
|
||||
|
||||
environment?: string;
|
||||
@@ -62,15 +63,13 @@ const DEFAULT_PROVIDER = {
|
||||
|
||||
class GithubAuth implements OAuthApi, SessionStateApi {
|
||||
static create({
|
||||
backendUrl,
|
||||
basePath,
|
||||
discoveryApi,
|
||||
environment = 'development',
|
||||
provider = DEFAULT_PROVIDER,
|
||||
oauthRequestApi,
|
||||
}: CreateOptions) {
|
||||
const connector = new DefaultAuthConnector({
|
||||
backendUrl,
|
||||
basePath,
|
||||
discoveryApi,
|
||||
environment,
|
||||
provider,
|
||||
oauthRequestApi: oauthRequestApi,
|
||||
|
||||
@@ -25,15 +25,17 @@ import {
|
||||
BackstageIdentity,
|
||||
AuthRequestOptions,
|
||||
} from '../../../definitions/auth';
|
||||
import { OAuthRequestApi, AuthProvider } from '../../../definitions';
|
||||
import {
|
||||
OAuthRequestApi,
|
||||
AuthProvider,
|
||||
DiscoveryApi,
|
||||
} from '../../../definitions';
|
||||
import { SessionManager } from '../../../../lib/AuthSessionManager/types';
|
||||
import { StaticAuthSessionManager } from '../../../../lib/AuthSessionManager';
|
||||
import { Observable } from '../../../../types';
|
||||
|
||||
type CreateOptions = {
|
||||
backendUrl: string;
|
||||
basePath: string;
|
||||
|
||||
discoveryApi: DiscoveryApi;
|
||||
oauthRequestApi: OAuthRequestApi;
|
||||
|
||||
environment?: string;
|
||||
@@ -58,15 +60,13 @@ const DEFAULT_PROVIDER = {
|
||||
|
||||
class GitlabAuth implements OAuthApi, SessionStateApi {
|
||||
static create({
|
||||
backendUrl,
|
||||
basePath,
|
||||
discoveryApi,
|
||||
environment = 'development',
|
||||
provider = DEFAULT_PROVIDER,
|
||||
oauthRequestApi,
|
||||
}: CreateOptions) {
|
||||
const connector = new DefaultAuthConnector({
|
||||
backendUrl,
|
||||
basePath,
|
||||
discoveryApi,
|
||||
environment,
|
||||
provider,
|
||||
oauthRequestApi,
|
||||
|
||||