Merge branch 'master' into add-saml-login

This commit is contained in:
J Shamsul Bahri (jibone))
2020-09-04 18:28:09 +08:00
823 changed files with 32014 additions and 5907 deletions
+1
View File
@@ -5,3 +5,4 @@
**/build/**
**/.git/**
**/public/**
**/microsite/**
+1 -1
View File
@@ -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 -->
+31 -1
View File
@@ -14,7 +14,37 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # Required to retrieve git history
- run: yarn install && yarn build-storybook
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
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') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
- run: yarn build-storybook
- uses: chromaui/action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,10 +1,11 @@
name: Frontend CI
name: CI
on:
pull_request:
paths-ignore:
- 'microsite/**'
jobs:
build:
verify:
runs-on: ubuntu-latest
strategy:
@@ -19,33 +20,54 @@ jobs:
- uses: actions/checkout@v2
- name: fetch branch master
run: git fetch origin master
- name: find location of global yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: cache global yarn cache
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: cache node_modules
uses: actions/cache@v2
with:
path: node_modules
key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
# Beginning of yarn setup, keep in sync between all workflows.
# TODO(Rugvip): move this to composite action once all features we use are supported
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
# Cache every node_modules folder inside the monorepo
- name: cache all node_modules
id: cache-modules
uses: actions/cache@v2
with:
path: '**/node_modules'
# We use both yarn.lock and package.json as cache keys to ensure that
# changes to local monorepo packages bust the cache.
key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }}
# If we get a cache hit for node_modules, there's no need to bring in the global
# yarn cache or run yarn install, as all dependencies will be installed already.
- 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') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
if: steps.cache-modules.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile
# End of yarn setup
- name: check for yarn.lock changes
id: yarn-lock
run: git diff --quiet origin/master HEAD -- yarn.lock
continue-on-error: true
- name: yarn install
run: yarn install --frozen-lockfile
- name: verify doc links
run: node docs/verify-links.js
- name: lint
run: yarn lerna -- run lint --since origin/master
@@ -55,8 +77,7 @@ jobs:
- name: build changed packages
if: ${{ steps.yarn-lock.outcome == 'success' }}
# Need to build all dependencies as well to be able to run tests later
run: yarn lerna -- run build --since origin/master --include-dependencies
run: yarn lerna -- run build --since origin/master
- name: build all packages
if: ${{ steps.yarn-lock.outcome == 'failure' }}
@@ -75,6 +96,3 @@ jobs:
- name: verify plugin template
run: yarn lerna -- run diff -- --check
- name: verify storybook
run: yarn workspace storybook build-storybook
@@ -1,12 +1,13 @@
name: CLI Test Windows
name: E2E Test Windows
# Building on windows is really slow, so this workflow is separate from cli.yml and only builds on changes
# Building on windows is really slow, so this workflow is separate from e2e.yml and only builds on changes
# to the cli itself. They're more likely to introduce issues on windows, compared to changes to core and yarn.lock.
on:
pull_request:
paths:
- '.github/workflows/cli-win.yml'
- '.github/workflows/e2e-win.yml'
- 'packages/cli/**'
- 'packages/e2e/**'
- 'packages/create-app/**'
jobs:
@@ -25,6 +26,13 @@ jobs:
name: Node ${{ matrix.node-version }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: find location of global yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
@@ -32,16 +40,15 @@ jobs:
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
- run: yarn tsc
- run: yarn build
- name: verify app and plugin creation
run: node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js
- name: yarn build
run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite
- name: run E2E test
run: yarn workspace e2e-test start
@@ -1,14 +1,9 @@
name: CLI Test
name: E2E Test Linux
on:
pull_request:
paths:
- '.github/workflows/cli.yml'
- 'packages/cli/**'
- 'packages/create-app/**'
- 'packages/core/**'
- 'packages/core-api/**'
- 'yarn.lock'
paths-ignore:
- 'microsite/**'
jobs:
build:
@@ -38,30 +33,44 @@ jobs:
name: Node ${{ matrix.node-version }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: find location of global yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: cache global yarn cache
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
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') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
- run: yarn tsc
- run: yarn build
- name: verify app and plugin creation
- name: yarn build
run: yarn build --ignore example-app --ignore example-backend --ignore @techdocs/cli --ignore backstage-microsite
- name: run E2E test
run: |
sudo sysctl fs.inotify.max_user_watches=524288
yarn workspace e2e-test start
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }}
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
run: |
sudo sysctl fs.inotify.max_user_watches=524288
node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js
+45
View File
@@ -0,0 +1,45 @@
name: Master Build Windows
on:
push:
branches: [master]
jobs:
build:
runs-on: windows-latest
strategy:
matrix:
node-version: [12.x]
env:
CI: true
NODE_OPTIONS: --max-old-space-size=4096
steps:
- uses: actions/checkout@v2
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: find location of global yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: cache global yarn cache
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
# Tests are broken on Windows, disabled for now
# - name: test
# run: yarn lerna -- run test
+30 -17
View File
@@ -1,4 +1,4 @@
name: Master Build
name: Main Master Build
on:
push:
@@ -18,29 +18,34 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: find location of global yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: cache global yarn cache
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: cache node_modules
uses: actions/cache@v2
with:
path: node_modules
key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
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') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
- name: lint
run: yarn lerna -- run lint
@@ -69,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}}'
@@ -1,16 +1,14 @@
name: Deploy Storybook
name: Build microsite
on:
push:
branches:
- master
pull_request:
paths:
- '.github/workflows/storybook-deploy.yml'
- 'packages/storybook/**'
- 'packages/core/src/**'
- '.github/workflows/microsite-build-check.yml'
- 'microsite/**'
- 'docs/**'
jobs:
deploy-storybook:
build-microsite:
runs-on: ubuntu-latest
strategy:
@@ -23,34 +21,34 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: find location of global yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: cache global yarn cache
uses: actions/cache@v2
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: cache node_modules
uses: actions/cache@v2
with:
path: node_modules
key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
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') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
run: yarn install --frozen-lockfile
- name: build storybook
run: yarn workspace storybook build-storybook
- name: deploy storybook to gh-pages
uses: JamesIves/github-pages-deploy-action@3.4.2
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: packages/storybook/dist
# End of yarn setup
- name: build microsite
run: yarn workspace backstage-microsite build
@@ -0,0 +1,74 @@
name: Deploy Microsite and Storybook
on:
push:
branches:
- master
paths:
- '.github/workflows/microsite-with-storybook-deploy.yml'
- 'packages/storybook/**'
- 'packages/core/src/**'
- 'microsite/**'
- 'docs/**'
jobs:
deploy-microsite-and-storybook:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
env:
CI: true
NODE_OPTIONS: --max-old-space-size=4096
steps:
- uses: actions/checkout@v2
# Beginning of yarn setup, keep in sync between all workflows, see ci.yml
- name: use node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
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') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: yarn install
run: yarn install --frozen-lockfile
# End of yarn setup
- name: build microsite
run: yarn workspace backstage-microsite build
- name: build storybook
run: yarn workspace storybook build-storybook
- name: move storybook dist into microsite
run: mv packages/storybook/dist/ microsite/build/backstage/storybook
- name: Check the build output
run: ls microsite/build/backstage && ls microsite/build/backstage/storybook
- name: Deploy both microsite and storybook to gh-pages
uses: JamesIves/github-pages-deploy-action@3.4.2
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: microsite/build/backstage
+4
View File
@@ -90,6 +90,10 @@ typings/
.nuxt
dist
# Microsite build output
microsite/build
microsite/i18n
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
+15 -13
View File
@@ -1,13 +1,15 @@
| Organization | Contact | Description of Use |
| --------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| [Spotify](https://www.spotify.com) | [@stefanalund](https://github.com/stefanalund) | Main interface towards all of Spotify's infrastructure and technical documentation. |
| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. |
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. |
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. |
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D |
| Organization | Contact | Description of Use |
| -------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| [Spotify](https://www.spotify.com) | [@stefanalund](https://github.com/stefanalund) | Main interface towards all of Spotify's infrastructure and technical documentation. |
| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. |
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. |
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. |
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D |
| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling |
| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling |
+27
View File
@@ -8,6 +8,33 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re
> Collect changes for the next release below
- The backend plugin
[service builder](https://github.com/spotify/backstage/blob/master/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts)
no longer adds `express.json()` automatically to all routes. While convenient
in a lot of cases, it also led to problems where for example the proxy
middleware could hang because the body had already been altered and could not
be streamed. Also, plugins that rather wanted to handle e.g. form encoded data
still had to cater to that manually. We therefore decided to let plugins add
`express.json()` themselves if they happen to deal with JSON data.
## 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
- Many plugins have been added to the catalog and will for now be required to be added to separate apps as well. This will be solved as [#1536](https://github.com/spotify/backstage/issues/1536) gets sorted out, but for now you may need to install some plugins just to get pages to work.
### @backstage/catalog-backend
- Added the possibility to add static locations via `app-config.yaml`. This changed the signature of `new LocationReaders(logger)` inside `packages/backend/src/plugins/catalog.ts` to `new LocationReaders({config, logger})`. [#1890](https://github.com/spotify/backstage/pull/1890)
### @backstage/theme
- Changed the type signature of the palette, removing `sidebar: string` and adding `navigation: { background: string; indicator: string}`. [#1880](https://github.com/spotify/backstage/pull/1880)
## v0.1.1-alpha.18
### @backstage/catalog-backend
+32 -4
View File
@@ -1,4 +1,4 @@
# Contributing
# Contributing to Backstage
Our vision for Backstage is for it to become the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We cant do it alone.
@@ -28,7 +28,7 @@ What kind of plugins should/could be created? Some inspiration from the 120+ plu
## Suggesting a plugin
If you start developing a plugin that you aim to release as open source, we suggest that you create a new [new Issue](https://github.com/spotify/backstage/issues/new?template=plugin_template.md). This helps the community know what plugins are in development.
If you start developing a plugin that you aim to release as open source, we suggest that you create a new [new Issue](https://github.com/spotify/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). This helps the community know what plugins are in development.
You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work.
@@ -38,7 +38,7 @@ The current documentation is very limited. Help us make the `/docs` folder come
## Contribute to Storybook
We think the best way to ensure different plugins provide a consistent experience is through a solid set of reusable UI/UX components. Backstage uses [Storybook](http://storybook.backstage.io).
We think the best way to ensure different plugins provide a consistent experience is through a solid set of reusable UI/UX components. Backstage uses [Storybook](http://backstage.io/storybook).
Either help us [create new components](https://github.com/spotify/backstage/labels/help%20wanted) or improve stories for the existing ones (look for files with `*.stories.tsx`).
@@ -60,7 +60,35 @@ Have you started using Backstage? Adding your company to [ADOPTERS](ADOPTERS.md)
# Get Started!
So...feel ready to jump in? Let's do this. Head over to the [Getting Started guide](https://github.com/spotify/backstage#getting-started) 👏🏻💯
So...feel ready to jump in? Let's do this. 👏🏻💯
To run a Backstage app, you will need to have the following installed:
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [NodeJS](https://nodejs.org/en/download/) - Active LTS Release, currently v12
- [yarn](https://classic.yarnpkg.com/en/docs/install)
After cloning this repo, open a terminal window and start the example app using the following commands from the project root:
```bash
yarn install # Install dependencies
yarn start # Start dev server, use --check to enable linting and type-checks
```
The final `yarn start` command should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal.
Depending on the work you're doing, you often also want to run the example backend. Start the backend in a separate terminal session using the following:
```bash
cd packages/backend
yarn start
yarn lerna run mock-data # Populate the backend with mock data
```
And that's it! You are good to go 👍
If you need help, just jump into our [Discord chatroom](https://discord.gg/MUpMjP2).
+34 -66
View File
@@ -1,4 +1,4 @@
![headline](docs/headline.png)
![headline](docs/assets/headline.png)
# [Backstage](https://backstage.io)
@@ -10,101 +10,69 @@
## What is Backstage?
[Backstage](https://backstage.io/) is an open platform for building developer portals. Its based on the developer portal weve been using internally at Spotify for over four years. Backstage can be as simple as a services catalog or as powerful as the UX layer for your entire tech infrastructure.
[Backstage](https://backstage.io/) is an open platform for building developer portals. Powered by a centralized service catalog, Backstage restores order to your microservices and infrastructure. So your product teams can ship high-quality code quickly — without compromising autonomy.
For more information go to [backstage.io](https://backstage.io) or join our [Discord chatroom](https://discord.gg/EBHEGzX).
### Features
- Create and manage all of your organizations software and microservices in one place.
- Services catalog keeps track of all software and its ownership.
- Visualizations provide information about your backend services and tooling, and help you monitor them.
- A unified method for managing microservices offers both visibility and control.
- Preset templates allow engineers to quickly create microservices in a standardized way ([coming soon](https://github.com/spotify/backstage/milestone/11)).
- Centralized, full-featured technical documentation with integrated tooling that makes it easy for developers to set up, publish, and maintain alongside their code ([coming soon](https://github.com/spotify/backstage/milestone/15)).
### Benefits
- 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_, its a single, consistent experience that ties all your infrastructure tooling, resources, standards, owners, contributors, and administrators together in one place.
## Backstage Service Catalog (alpha)
The Backstage Service Catalog — actually, a software catalog, since it includes more than just services — is a centralized system that keeps track of ownership and metadata for all the software in your ecosystem (services, websites, libraries, data pipelines, etc). The catalog is built around the concept of [metadata yaml files](https://github.com/spotify/backstage/blob/master/docs/architecture-decisions/adr002-default-catalog-file-format.md#format) stored together with the code, which are then harvested and visualized in Backstage.
Backstage unifies all your infrastructure tooling, services, and documentation to create a streamlined development environment from end to end.
![service-catalog](https://backstage.io/blog/assets/6/header.png)
We have also found that the service catalog is a great way to organise the infrastructure tools you use to manage the software as well. This is how Backstage creates one developer portal for all your tools. Rather than asking teams to jump between different infrastructure UIs (and incurring additional cognitive overhead each time they make a context switch), most of these tools can be organised around the entities in the catalog.
Out of the box, Backstage includes:
- [Backstage Service Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview) for managing all your software (microservices, libraries, data pipelines, websites, ML models, etc.)
- [Backstage Software Templates](https://backstage.io/docs/features/software-templates/software-templates-index) for quickly spinning up new projects and standardizing your tooling with your organizations best practices
- [Backstage TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview) 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 Backstages customizability and functionality
For more information go to [backstage.io](https://backstage.io) or join our [Discord chatroom](https://discord.gg/EBHEGzX).
## Project roadmap
We created Backstage about 4 years ago. While our internal version of Backstage has had the benefit of time to mature and evolve, the first iteration of our open source version is still nascent. We are envisioning three phases of the project and we have already begun work on various aspects of these phases:
A detailed project roadmap, including already delivered milestones, is available [here](https://backstage.io/docs/overview/roadmap).
- 🐣 **Phase 1:** Extensible frontend platform (Done ✅) - You will be able to easily create a single consistent UI layer for your internal infrastructure and tools. A set of reusable [UX patterns and components](http://storybook.backstage.io) help ensure a consistent experience between tools.
## Getting Started
- 🐢 **Phase 2:** Service Catalog ([alpha released](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)) - With a single catalog, Backstage makes it easy for a team to manage ten services — and makes it possible for your company to manage thousands of them. Developers can get a uniform overview of all their software and related resources, regardless of how and where they are running, as well as an easy way to onboard and manage those resources.
There are two different ways to get started with Backstage, either by creating a standalone app, or by cloning this repo. Which method you use depends on what you're planning to do.
- 🐇 **Phase 3:** Ecosystem (later) - Everyone's infrastructure stack is different. By fostering a vibrant community of contributors we hope to provide an ecosystem of Open Source plugins/integrations that allows you to pick the tools that match your stack.
Creating a standalone instance makes it simpler to customize the application for your needs whilst staying up to date with the project. You will also depend on `@backstage` packages from NPM, making the project much smaller. This is the recommended approach if you want to kick the tyres of Backstage or setup your own instance.
Check out our [Milestones](https://github.com/spotify/backstage/milestones) and open [RFCs](https://github.com/spotify/backstage/labels/rfc) how they relate to the three Phases outlined above.
On the other hand, if you want to contribute plugins or to the project in general, it's easier to fork and clone this project. That will let you stay up to date with the latest changes, and gives you an easier path to make Pull Requests towards this repo.
Our vision for Backstage is for it to become the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We cant do it alone. If this sounds interesting or you'd like to help us shape our product vision, we'd love to talk. You can email me directly: [alund@spotify.com](mailto:alund@spotify.com).
### Creating a Standalone App
## Overview
Backstage provides the `@backstage/create-app` package to scaffold standalone instances of Backstage. You will need to have
[NodeJS](https://nodejs.org/en/download/) Active LTS Release installed
(currently v12), and [yarn](https://classic.yarnpkg.com/en/docs/install). You will also need to have [Docker](https://docs.docker.com/engine/install/) installed to use some features like Software Templates and TechDocs.
The Backstage platform consists of a number of different components:
- **app** - Main web application that users interact with. It's built up by a number of different _Plugins_. This repo contains an example implementation of an app (located in `packages/app`) and you can easily get started with your own app by [creating one](docs/getting-started/create-an-app.md).
- [**plugins**](https://github.com/spotify/backstage/tree/master/plugins) - Each plugin is treated as a self-contained web app and can include almost any type of content. Plugins all use a common set of platform API's and reusable UI components. Plugins can fetch data either from the _backend_ or through any RESTful API exposed through the _proxy_.
- [**service catalog**](https://github.com/spotify/backstage/tree/master/packages/backend) - Service that holds the model of your software ecosystem, including organisational information and what team owns what software. The backend also has a Plugin model for extending its graph.
- [**proxy**](https://github.com/spotify/backstage/tree/master/plugins/proxy-backend) - Terminates HTTPS and exposes any RESTful API to Plugins.
- **identity** - A backend service that holds your organisation's metadata.
## Getting started
To run a Backstage app, you will need to have the following installed:
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [NodeJS](https://nodejs.org/en/download/) - Active LTS Release, currently v12
- [yarn](https://classic.yarnpkg.com/en/docs/install)
After cloning this repo, open a terminal window and start the example app using the following commands from the project root:
Using `npx` you can then run the following to create an app in a chosen subdirectory of your current working directory:
```bash
yarn install # Install dependencies
yarn start # Start dev server, use --check to enable linting and type-checks
npx @backstage/create-app
```
The final `yarn start` command should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal.
You will be taken through a wizard to create your app, and the output should look something like this. You can read more about this process [here](https://backstage.io/docs/getting-started/create-an-app)
And that's it! You are good to go 👍
### Contributing to Backstage
### Next step
You can read more in our [CONTRIBUTING.md](./CONTRIBUTING.md#get-started) guide, which can help you get setup with a Backstage development environment.
Take a look at the [Getting Started](docs/getting-started/index.md) guide to learn how to set up Backstage, and how to develop on the platform.
### Next steps
Take a look at the [Getting Started](https://backstage.io/docs/getting-started/index) guide to learn how to set up Backstage, and how to develop on the platform.
## Documentation
- [Main documentation](docs/README.md)
- [Service Catalog](docs/features/software-catalog/index.md)
- [Create a Backstage App](docs/getting-started/create-an-app.md)
- [Architecture](docs/overview/architecture-terminology.md) ([Decisions](docs/architecture-decisions/index.md))
- [Designing for Backstage](docs/dls/design.md)
- [Storybook - UI components](http://storybook.backstage.io)
## Contributing
We would love your help in building Backstage! See [CONTRIBUTING](CONTRIBUTING.md) for more information.
- [Main documentation](https://backstage.io/docs)
- [Service Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview)
- [Architecture](https://backstage.io/docs/overview/architecture-terminology) ([Decisions](https://backstage.io/docs/architecture-decisions/adrs-overview))
- [Designing for Backstage](https://backstage.io/docs/dls/design)
- [Storybook - UI components](https://backstage.io/storybook)
## Community
- [Discord chatroom](https://discord.gg/MUpMjP2) - Get support or discuss the project
- [Good First Issues](https://github.com/spotify/backstage/contribute) - Start here if you want to contribute
- [RFCs](https://github.com/spotify/backstage/labels/rfc) - Help shape the technical direction
- [FAQ](docs/FAQ.md) - Frequently Asked Questions
- [FAQ](https://backstage.io/docs/FAQ) - Frequently Asked Questions
- [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll
- [Adopters](ADOPTERS.md) - Companies already using Backstage
- [Blog](https://backstage.io/blog/) - Announcements and updates
+111 -8
View File
@@ -4,18 +4,25 @@ app:
backend:
baseUrl: http://localhost:7000
listen: 0.0.0.0:7000
listen:
port: 7000
cors:
origin: http://localhost:3000
methods: [GET, POST, PUT, DELETE]
credentials: true
database:
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
headers:
Authorization:
$secret:
env: JENKINS_BASIC_AUTH_HEADER
organization:
name: Spotify
@@ -26,12 +33,70 @@ techdocs:
sentry:
organization: spotify
rollbar:
organization: spotify
accountToken:
$secret:
env: ROLLBAR_ACCOUNT_TOKEN
newrelic:
api:
baseUrl: 'https://api.newrelic.com/v2'
key: NEW_RELIC_REST_API_KEY
lighthouse:
baseUrl: http://localhost:3003
catalog:
rules:
- allow: [Component, API, Group, Template, Location]
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
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/hello-world-api.yaml
- https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/streetlights-api.yaml
auth:
providers:
google:
development:
<<<<<<< HEAD
appOrigin: 'http://localhost:3000/'
secure: false
=======
>>>>>>> master
clientId:
$secret:
env: AUTH_GOOGLE_CLIENT_ID
@@ -40,8 +105,11 @@ auth:
env: AUTH_GOOGLE_CLIENT_SECRET
github:
development:
<<<<<<< HEAD
appOrigin: 'http://localhost:3000/'
secure: false
=======
>>>>>>> master
clientId:
$secret:
env: AUTH_GITHUB_CLIENT_ID
@@ -53,8 +121,11 @@ auth:
env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
gitlab:
development:
<<<<<<< HEAD
appOrigin: 'http://localhost:3000/'
secure: false
=======
>>>>>>> master
clientId:
$secret:
env: AUTH_GITLAB_CLIENT_ID
@@ -65,6 +136,7 @@ auth:
$secret:
env: GITLAB_BASE_URL
saml:
<<<<<<< HEAD
development:
entryPoint: 'http://localhost:7001/'
issuer: 'passport-saml'
@@ -72,6 +144,12 @@ auth:
development:
appOrigin: 'http://localhost:3000/'
secure: false
=======
entryPoint: "http://localhost:7001/"
issuer: "passport-saml"
okta:
development:
>>>>>>> master
clientId:
$secret:
env: AUTH_OKTA_CLIENT_ID
@@ -83,17 +161,42 @@ auth:
env: AUTH_OKTA_AUDIENCE
oauth2:
development:
<<<<<<< HEAD
appOrigin: 'http://localhost:3000/'
secure: false
=======
>>>>>>> master
clientId:
$secret:
env: AUTH_OAUTH2_CLIENT_ID
clientSecret:
$secret:
env: AUTH_OAUTH2_CLIENT_SECRET
authorizationURL:
authorizationUrl:
$secret:
env: AUTH_OAUTH2_AUTH_URL
tokenURL:
tokenUrl:
$secret:
env: AUTH_OAUTH2_TOKEN_URL
auth0:
development:
clientId:
$secret:
env: AUTH_AUTH0_CLIENT_ID
clientSecret:
$secret:
env: AUTH_AUTH0_CLIENT_SECRET
domain:
$secret:
env: AUTH_AUTH0_DOMAIN
microsoft:
development:
clientId:
$secret:
env: AUTH_MICROSOFT_CLIENT_ID
clientSecret:
$secret:
env: AUTH_MICROSOFT_CLIENT_SECRET
tenantId:
$secret:
env: AUTH_MICROSOFT_TENANT_ID
+1
View File
@@ -7,6 +7,7 @@ metadata:
annotations:
github.com/project-slug: spotify/backstage
backstage.io/github-actions-id: spotify/backstage
backstage.io/techdocs-ref: github:https://github.com/spotify/backstage.git
spec:
type: library
owner: Spotify
+52 -32
View File
@@ -1,6 +1,9 @@
# FAQ
---
id: FAQ
title: FAQ
---
## Product FAQ:
## Product FAQ
### Can we call Backstage something different? So that it fits our company better?
@@ -14,8 +17,7 @@ brand.
No, but it can be! Backstage is designed to be a developer portal for all your
infrastructure tooling, services, and documentation. So, it's not a monitoring
platform — but that doesn't mean you can't integrate a monitoring tool into
Backstage by writing
[a plugin](/docs/FAQ.md#what-is-a-plugin-in-backstage).
Backstage by writing [a plugin](#what-is-a-plugin-in-backstage).
### How is Backstage licensed?
@@ -36,12 +38,11 @@ more, read our blog post,
Yes, we've already started releasing open source versions of some of the plugins
we use here, and we'll continue to do so.
[Plugins](/docs/FAQ.md#what-is-a-plugin-in-backstage) are the
building blocks of functionality in Backstage. We have over 120 plugins inside
Spotify — many of those are specialized for our use, so will remain internal and
proprietary to us. But we estimate that about a third of our existing plugins
make good open source candidates. (And we'll probably end up writing some brand
new ones, too.)
[Plugins](#what-is-a-plugin-in-backstage) are the building blocks of
functionality in Backstage. We have over 120 plugins inside Spotify — many of
those are specialized for our use, so will remain internal and proprietary to
us. But we estimate that about a third of our existing plugins make good open
source candidates. (And we'll probably end up writing some brand new ones, too.)
### What's the roadmap for Backstage?
@@ -66,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?
@@ -91,21 +92,31 @@ Node.js and GraphQL.
### What is the end-to-end user flow? The happy path story.
There are three main user profiles for Backstage: the integrator, the
contributor, and the software engineer.
contributor, and the software engineer.
The **integrator** hosts the Backstage app and configures which plugins are available to use in the app.
The **integrator** hosts the Backstage app and configures which plugins are
available to use in the app.
The **contributor** adds functionality to the app by writing plugins.
The **software engineer** uses the app's functionality and interacts with its plugins.
The **software engineer** uses the app's functionality and interacts with its
plugins.
### What is a "plugin" in Backstage?
Plugins are what provide the feature functionality in Backstage. They are used to integrate different systems into Backstage's frontend, so that the developer gets a consistent UX, no matter what tool or service is being accessed on the other side.
Plugins are what provide the feature functionality in Backstage. They are used
to integrate different systems into Backstage's frontend, so that the developer
gets a consistent UX, no matter what tool or service is being accessed on the
other side.
Each plugin is treated as a self-contained web app and can include almost any type of content. Plugins all use a common set of platform APIs and reusable UI components. Plugins can fetch data either from the backend or an API exposed through the proxy.
Each plugin is treated as a self-contained web app and can include almost any
type of content. Plugins all use a common set of platform APIs and reusable UI
components. Plugins can fetch data either from the backend or an API exposed
through the proxy.
Learn more about [the different components](https://github.com/spotify/backstage#overview) that make up Backstage.
Learn more about
[the different components](https://github.com/spotify/backstage#overview) that
make up Backstage.
### Do I have to write plugins in TypeScript?
@@ -127,7 +138,15 @@ can browse and search for all available plugins.
### Which plugin is used the most at Spotify?
By far, our most-used plugin is our TechDocs plugin, which we use for creating technical documentation. Our philosophy at Spotify is to treat "docs like code", where you write documentation using the same workflow as you write your code. This makes it easier to create, find, and update documentation. We hope to release [the open source version](https://github.com/spotify/backstage/issues/687) in the future. (See also: "[Will Spotify's internal plugins be open sourced, too?](/docs/FAQ.md#will-spotifys-internal-plugins-be-open-sourced-too)" above)
By far, our most-used plugin is our TechDocs plugin, which we use for creating
technical documentation. Our philosophy at Spotify is to treat "docs like code",
where you write documentation using the same workflow as you write your code.
This makes it easier to create, find, and update documentation. We hope to
release
[the open source version](https://github.com/spotify/backstage/issues/687) in
the future. (See also:
"[Will Spotify's internal plugins be open sourced, too?](#will-spotifys-internal-plugins-be-open-sourced-too)"
above)
### Are you planning to have plugins baked into the repo? Or should they be developed in separate repos?
@@ -135,10 +154,10 @@ Contributors can add open source plugins to the plugins directory in
[this monorepo](https://github.com/spotify/backstage). Integrators can then
configure which open source plugins are available to use in their instance of
the app. Open source plugins are downloaded as npm packages published in the
open source repository. While we encourage using the open source model, we
know there are cases where contributors might want to experiment internally or
keep their plugins closed source. Contributors writing closed source plugins
should develop them in the plugins directory in their own Backstage repository.
open source repository. While we encourage using the open source model, we know
there are cases where contributors might want to experiment internally or keep
their plugins closed source. Contributors writing closed source plugins should
develop them in the plugins directory in their own Backstage repository.
Integrators also configure closed source plugins locally from the monorepo.
### Any plans for integrating with other repository managers, such as GitLab or Bitbucket?
@@ -149,7 +168,8 @@ stage. Hosting this project on GitHub does not exclude integrations with
alternatives, such as
[GitLab](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+GitLab)
or Bitbucket. We believe that in time there will be plugins that will provide
functionality for these tools as well. Hopefully, contributed by the community! Also note, implementations of Backstage can be hosted wherever you feel suits
functionality for these tools as well. Hopefully, contributed by the community!
Also note, implementations of Backstage can be hosted wherever you feel suits
your needs best.
### Who maintains Backstage?
@@ -165,8 +185,8 @@ maintains Backstage in your own environment.
### Does Spotify provide a managed version of Backstage?
No, this is not a service offering. We build the piece of software, and
someone in your infrastructure team is responsible for
No, this is not a service offering. We build the piece of software, and someone
in your infrastructure team is responsible for
[deploying](https://github.com/spotify/backstage/blob/master/DEPLOYMENT.md) and
maintaining it.
@@ -186,16 +206,16 @@ Please report sensitive security issues via Spotify's
No. Backstage does not collect any telemetry from any third party using the
platform. Spotify, and the open source community, does have access to
[GitHub Insights](https://github.com/features/insights), which contains
information such as contributors, commits, traffic, and dependencies.
Backstage is an open platform, but you are in control of your own data. You
control who has access to any data you provide to your version of Backstage and
who that data is shared with.
information such as contributors, commits, traffic, and dependencies. Backstage
is an open platform, but you are in control of your own data. You control who
has access to any data you provide to your version of Backstage and who that
data is shared with.
### Can Backstage be used to build something other than a developer portal?
Yes. The core frontend framework could be used for building any large-scale
web application where (1) multiple teams are building separate parts of the app,
and (2) you want the overall experience to be consistent. That being said, in
Yes. The core frontend framework could be used for building any large-scale web
application where (1) multiple teams are building separate parts of the app, and
(2) you want the overall experience to be consistent. That being said, in
[Phase 2](https://github.com/spotify/backstage#project-roadmap) of the project
we will add features that are needed for developer portals and systems for
managing software ecosystems. Our ambition will be to keep Backstage modular.
+2 -105
View File
@@ -1,106 +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.
- 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)
- 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)
- [Populating the catalog](features/software-catalog/populating.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)
- [Reading Documentation](features/techdocs/reading-documentation.md)
- [Writing Documentation](features/techdocs/writing-documentation.md)
- [Publishing Documentation](features/techdocs/publishing-documentation.md)
- [Contributing](features/techdocs/contributing.md)
- [Debugging](features/techdocs/debugging.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://storybook.backstage.io)
- [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
+6
View File
@@ -0,0 +1,6 @@
---
id: backend
title: Backend
---
## TODO
+16 -8
View File
@@ -1,4 +1,7 @@
# Utility APIs
---
id: utility-apis
title: Utility APIs
---
## Introduction
@@ -68,18 +71,23 @@ import {
AlertApiForwarder,
ErrorApiForwarder,
ErrorAlerter,
ConfigApi
} from '@backstage/core';
const builder = ApiRegistry.builder();
// The alert API is a self-contained implementation that shows alerts to the user.
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
const apis = (config: ConfigApi) => {
const builder = ApiRegistry.builder();
// The error API uses the alert API to send error notifications to the user.
builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder()));
// The alert API is a self-contained implementation that shows alerts to the user.
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
// The error API uses the alert API to send error notifications to the user.
builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder()));
return builder.build();
}
const app = createApp({
apis: apiBuilder.build(),
apis,
// ... other config
});
```
@@ -153,7 +161,7 @@ The figure below shows the relationship between
<span style="color: #b85450">fooApiRef</span>.
<div style="text-align:center">
<img src="utility-apis-fig1.svg" alt="Figure showing the relationship between utility APIs, the apps that provide them, and the plugins that consume them">
<img src="../assets/utility-apis-fig1.svg" alt="Figure showing the relationship between utility APIs, the apps that provide them, and the plugins that consume them">
</div>
The current method for connecting Utility API providers and consumers is via the
@@ -1,4 +1,8 @@
# ADR001: Architecture Decision Record (ADR) log
---
id: adrs-adr001
title: ADR001: Architecture Decision Record (ADR) log
sidebar_label: ADR001
---
| Created | Status |
| ---------- | ------ |
@@ -1,4 +1,8 @@
# ADR002: Default Software Catalog File Format
---
id: adrs-adr002
title: ADR002: Default Software Catalog File Format
sidebar_label: ADR002
---
| Created | Status |
| ---------- | ------ |
@@ -1,4 +1,8 @@
# ADR003: Avoid Default Exports and Prefer Named Exports
---
id: adrs-adr003
title: ADR003: Avoid Default Exports and Prefer Named Exports
sidebar_label: ADR003
---
| Created | Status |
| ---------- | ------ |
@@ -1,4 +1,8 @@
# ADR004: Module Export Structure
---
id: adrs-adr004
title: ADR004: Module Export Structure
sidebar_label: ADR004
---
| Created | Status |
| ---------- | ------ |
@@ -1,4 +1,8 @@
# ADR005: Catalog Core Entities
---
id: adrs-adr005
title: ADR005: Catalog Core Entities
sidebar_label: ADR005
---
| Created | Status |
| ---------- | ------ |
@@ -18,7 +22,7 @@ Backstage should eventually support the following core entities:
- **Resources** are physical or virtual infrastructure needed to operate a
component
![Catalog Core Entities](catalog-core-entities.png)
![Catalog Core Entities](../assets/architecture-decisions/catalog-core-entities.png)
For now, we'll start by only implementing support for the Component entity in
the Backstage catalog. This can later be extended to APIs, Resources and other
@@ -1,4 +1,8 @@
# ADR006: Avoid React.FC and React.SFC
---
id: adrs-adr006
title: ADR006: Avoid React.FC and React.SFC
sidebar_label: ADR006
---
## Context
@@ -1,4 +1,8 @@
# ADR007: Use MSW to mock http requests
---
id: adrs-adr007
title: ADR007: Use MSW to mock http requests
sidebar_label: ADR007
---
## Context
@@ -1,4 +1,8 @@
# ADR008: Default Catalog File Name
---
id: adrs-adr008
title: ADR008: Default Catalog File Name
sidebar_label: ADR008
---
## Background
+9 -2
View File
@@ -1,4 +1,8 @@
# Architecture Decision Records (ADR)
---
id: adrs-overview
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
@@ -19,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: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 181 KiB

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 265 KiB

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 204 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 293 KiB

After

Width:  |  Height:  |  Size: 293 KiB

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

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: 691 KiB

After

Width:  |  Height:  |  Size: 691 KiB

Before

Width:  |  Height:  |  Size: 389 KiB

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+108 -54
View File
@@ -1,4 +1,7 @@
# Adding authentication providers
---
id: add-auth-provider
title: Adding authentication providers
---
## Passport
@@ -45,22 +48,35 @@ provider class which implements a handler for the chosen framework.
#### Adding an OAuth based provider
If we're adding an `OAuth` based provider we would implement the
[OAuthProviderHandlers](#OAuthProviderHandlers) interface.
[OAuthProviderHandlers](#OAuthProviderHandlers) interface. By implementing this
interface we can use the `OAuthProvider` class provided by `lib/oauth`, meaning
we don't need to implement the full
[AuthProviderRouteHandlers](#AuthProviderRouteHandlers) interface that providers
otherwise need to implement.
The provider class takes the provider's configuration as a class parameter. It
also imports the `Strategy` from the passport package.
The provider class takes the provider's options as a class parameter. It also
imports the `Strategy` from the passport package.
```ts
import { Strategy as ProviderAStrategy } from 'passport-provider-a';
export type ProviderAProviderOptions = OAuthProviderOptions & {
// extra options here
}
export class ProviderAAuthProvider implements OAuthProviderHandlers {
private readonly providerConfig: AuthProviderConfig;
private readonly _strategy: ProviderAStrategy;
constructor(providerConfig: AuthProviderConfig) {
this.providerConfig = providerConfig;
constructor(options: ProviderAProviderOptions) {
this._strategy = new ProviderAStrategy(
{ ...providerConfig.options },
{
clientID: options.clientId,
clientSecret: options.clientSecret,
callbackURL: options.callbackUrl,
passReqToCallback: false as true,
response_type: 'code',
/// ... etc
}
verifyFunction, // See the "Verify Callback" section
);
}
@@ -79,14 +95,18 @@ An non-`OAuth` based provider could implement
[AuthProviderRouteHandlers](#AuthProviderRouteHandlers) instead.
```ts
type ProviderAOptions = {
// ...
};
export class ProviderAAuthProvider implements AuthProviderRouteHandlers {
private readonly providerConfig: AuthProviderConfig;
private readonly _strategy: ProviderAStrategy;
constructor(providerConfig: AuthProviderConfig) {
this.providerConfig = providerConfig;
constructor(options: ProviderAOptions) {
this._strategy = new ProviderAStrategy(
{ ...providerConfig.options },
{
// ...
},
verifyFunction, // See the "Verify Callback" section
);
}
@@ -98,31 +118,61 @@ export class ProviderAAuthProvider implements AuthProviderRouteHandlers {
}
```
#### Create method
#### Factory function
Each provider exports a create method that creates the provider instance,
optionally extending a supported authorization framework. This method exists to
allow for flexibility if additional frameworks are supported in the future.
Each provider exports a factory function that instantiates the provider. The
factory should implement [AuthProviderFactory](#AuthProviderFactory), which
passes in a object with utilities for configuration, logging, token issuing,
etc. The factory should return an implementation of
[AuthProviderRouteHandlers](#AuthProviderRouteHandlers).
Implementing OAuth by returning an instance of `OAuthProvider` based of the
provider's class:
The factory is what decides the mapping from
[static configuration](../conf/index.md) to the creation of auth providers. For
example, OAuth providers use `OAuthEnvironmentHandler` to allow for multiple
different configurations, one for each environment, which looks like this;
```ts
export function createProviderAProvider(config: AuthProviderConfig) {
const provider = new ProviderAAuthProvider(config);
const oauthProvider = new OAuthProvider(provider, config.provider, true);
return oauthProvider;
}
export const createOktaProvider: AuthProviderFactory = ({
globalConfig,
config,
tokenIssuer,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
// read options from config
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
// instantiate our OAuthProviderHandlers implementation
const provider = new OktaAuthProvider({
audience,
clientId,
clientSecret,
callbackUrl,
});
// Wrap the OAuthProviderHandlers with OAuthProvider, which implements AuthProviderRouteHandlers
return OAuthProvider.fromConfig(globalConfig, provider, {
disableRefresh: false,
providerId,
tokenIssuer,
});
});
```
Not extending with OAuth, the main difference here is that the create method is
returning a instance of the class without adding the OAuth authorization
framework to it.
The purpose of the different environments is to allow for a single auth-backend
to serve as the authentication service for multiple different frontend
environments, such as local development, staging, and production.
The factory function for other providers can be a lot simpler, as they might not
have configuration for each environment. Looking something like this:
```ts
export function createProviderAProvider(config: AuthProviderConfig) {
return new ProviderAAuthProvider(config);
}
export const createProviderAProvider: AuthProviderFactory = ({ config }) => {
const a = config.getString('a');
const b = config.getString('b');
return new ProviderAAuthProvider({ a, b });
};
```
#### Verify Callback
@@ -141,7 +191,7 @@ export function createProviderAProvider(config: AuthProviderConfig) {
> http://www.passportjs.org/docs/configure/
**`plugins/auth-backend/src/providers/providerA/index.ts`** is simply
re-exporting the create method to be used for hooking the provider up to the
re-exporting the factory function to be used for hooking the provider up to the
backend.
```ts
@@ -150,26 +200,14 @@ export { createProviderAProvider } from './provider';
### Hook it up to the backend
**`plugins/auth-backend/src/providers/config.ts`** The provider needs to be
configured properly so you need to add it to the list of configured providers,
all of which implement [AuthProviderConfig](#AuthProviderConfig):
```ts
export const providers = [
{
provider: 'providerA', # used as an identifier
options: { ... }, # consult the provider documentation for which options you should provide
disableRefresh: true # if the provider lacks refresh tokens
},
```
**`plugins/auth-backend/src/providers/factories.ts`** When the `auth-backend`
starts it sets up routing for all the available providers by calling
`createAuthProviderRouter` on each provider. You need to import the create
method from the provider and add it to the factory:
`createAuthProviderRouter` on each provider. You need to import the factory
function from the provider and add it to the factory:
```ts
import { createProviderAProvider } from './providerA';
const factories: { [providerId: string]: AuthProviderFactory } = {
providerA: createProviderAProvider,
};
@@ -200,10 +238,21 @@ web browser and you should be able to trigger the authorization flow.
```ts
export interface OAuthProviderHandlers {
start(req: express.Request, options: any): Promise<any>;
handler(req: express.Request): Promise<any>;
refresh?(refreshToken: string, scope: string): Promise<any>;
logout?(): Promise<any>;
start(
req: express.Request,
options: Record<string, string>,
): Promise<RedirectInfo>;
handler(
req: express.Request,
): Promise<{
response: AuthResponse<OAuthProviderInfo>;
refreshToken?: string;
}>;
refresh?(
refreshToken: string,
scope: string,
): Promise<AuthResponse<OAuthProviderInfo>>;
logout?(): Promise<void>;
}
```
@@ -218,12 +267,17 @@ export interface AuthProviderRouteHandlers {
}
```
##### AuthProviderConfig
##### AuthProviderFactory
```ts
export type AuthProviderConfig = {
provider: string;
options: any;
disableRefresh?: boolean;
export type AuthProviderFactoryOptions = {
globalConfig: AuthProviderConfig;
config: Config;
logger: Logger;
tokenIssuer: TokenIssuer;
};
export type AuthProviderFactory = (
options: AuthProviderFactoryOptions,
) => AuthProviderRouteHandlers;
```
+132
View File
@@ -0,0 +1,132 @@
---
id: auth-backend-classes
title: Auth backend classes
---
## How Does Authentication Work?
The Backstage application can use various authentication providers for
authentication. A provider has to implement an `AuthProviderRouteHandlers`
interface for handling authentication. This interface consists of four methods.
Each of these methods is hosted at an endpoint `/auth/[provider]/method`, where
`method` performs a certain operation as follows:
```
/auth/[provider]/start -> start
/auth/[provider]/handler/frame -> frameHandler
/auth/[provider]/refresh -> refresh
/auth/[provider]/logout -> logout
```
For more information on how these methods are used and for which purpose, refer
to the documentation [here](oauth.md).
For details on the parameters, input and output conditions for each method,
refer to the type documentation under
`plugins/auth-backend/src/providers/types.ts`.
There are currently two different classes for two authentication mechanisms that
implement this interface: an `OAuthAdapter` for [OAuth](https://oauth.net/2/)
based mechanisms and a `SAMLAuthProvider` for
[SAML](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html)
based mechanisms.
### OAuth mechanisms
Currently OAuth is assumed to be the de facto authentication mechanism for
Backstage based applications.
Backstage comes with a "batteries-included" set of supported commonly used OAuth
providers: Okta, Github, Google, Gitlab, and a generic OAuth2 provider.
All of these use the authorization flow of OAuth2 to implement authentication.
If your authentication provider is any of the above mentioned (except generic
OAuth2) providers, you can configure them by setting the right variables in
`app-config.yaml` under the `auth` section.
### Configuration
Each authentication provider (except SAML) needs five parameters: an OAuth
client ID, a client secret, an authorization endpoint and a token endpoint, and
an app origin. The app origin is the URL at which the frontend of the
application is hosted, and it is read from the `app.baseUrl` config. This is
required because the application opens a popup window to perform the
authentication, and once the flow is completed, the popup window sends a
`postMessage` to the frontend application to indicate the result of the
operation. Also this URL is used to verify that authentication requests are
coming from only this endpoint.
These values are configured via the `app-config.yaml` present in the root of
your app folder.
```
auth:
providers:
google:
development:
clientId:
$secret:
env: AUTH_GOOGLE_CLIENT_ID
clientSecret:
$secret:
env: AUTH_GOOGLE_CLIENT_SECRET
github:
development:
clientId:
$secret:
env: AUTH_GITHUB_CLIENT_ID
clientSecret:
$secret:
env: AUTH_GITHUB_CLIENT_SECRET
enterpriseInstanceUrl:
$secret:
env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
gitlab:
development:
clientId:
$secret:
...
```
## Technical Notes
### OAuthEnvironmentHandler
The concept of an "env" is core to the way the auth backend works. It uses an
`env` query parameter to identify the environment in which the application is
running (`development`, `staging`, `production`, etc). Each runtime can support
multiple environments at the same time and the right handler for each request is
identified and dispatched to based on the `env` parameter. All
`AuthProviderRouteHandlers` are wrapped within an `OAuthEnvironmentHandler`.
To instantiate multiple OAuth providers for different environments, use
`OAuthEnvironmentHandler.mapConfig`. It's a helper to iterate over a
configuration object that is a map of environment to configurations. See one of
the existing OAuth providers for an example of how it is used.
Given the following configuration:
```yaml
development:
clientId: abc
clientSecret: secret
production:
clientId: xyz
clientSecret: supersecret
```
The `OAuthEnvironmentHandler.mapConfig(config, envConfig => ...)` call will
split the `config` by the top level `development` and `production` keys, and
pass on each block as `envConfig`.
For a list of currently available providers, look in the `factories` module
located in `plugins/auth-backend/src/providers/factories.ts`
### OAuth2 provider
The `oauth2` provider abstracts a generic **OAuth2 + OIDC** based authentication
provider. What this means is that after the application has been given
permission by the user, the `authorization code` will be exchanged for an
`access_token`, a `refresh_token` and an `id_token`. This `id_token` is used to
obtain an email id of the user, which is then used for creating the session.
+6
View File
@@ -0,0 +1,6 @@
---
id: auth-backend
title: Auth backend
---
## TODO
+4 -1
View File
@@ -1,4 +1,7 @@
# Glossary
---
id: glossary
title: Glossary
---
- **Popup** - A separate browser window opened on top of the previous one.
- **OAuth** - More specifically OAuth 2.0, a standard protocol for
+4 -1
View File
@@ -1,4 +1,7 @@
# User Authentication and Authorization in Backstage
---
id: index
title: User Authentication and Authorization in Backstage
---
## Summary
+6 -3
View File
@@ -1,4 +1,7 @@
# OAuth and OpenID Connect
---
id: oauth
title: OAuth and OpenID Connect
---
This section describes how Backstage allows plugins to request OAuth Access
Tokens and OpenID Connect ID Tokens on behalf of the user, to be used for auth
@@ -101,6 +104,8 @@ request an access token.
The following diagram visualizes the flow described in the previous section.
![](oauth-popup-flow.svg)
<!--
@startuml oauth-popup-flow
@@ -149,5 +154,3 @@ Browser <- Backend: Tokens and info
@enduml
-->
![](oauth-popup-flow.svg)
+4 -1
View File
@@ -1,4 +1,7 @@
# Defining Configuration for your Plugin
---
id: defining
title: Defining Configuration for your Plugin
---
There is currently no tooling support or helpers for defining plugin
configuration. But it's on the roadmap.
+4 -1
View File
@@ -1,4 +1,7 @@
# Static Configuration in Backstage
---
id: index
title: Static Configuration in Backstage
---
## Summary
+5 -2
View File
@@ -1,4 +1,7 @@
# Reading Backstage Configuration
---
id: reading
title: Reading Backstage Configuration
---
## Config API
@@ -98,7 +101,7 @@ A good pattern for reading optional configuration values is to use the `??`
operator. For example:
```ts
const title = config.getString('my-plugin.title') ?? 'My Plugin';
const title = config.getOptionalString('my-plugin.title') ?? 'My Plugin';
```
To read required configuration, simply use the methods without `Optional`, for
+4 -1
View File
@@ -1,4 +1,7 @@
# Writing Backstage Configuration Files
---
id: writing
title: Writing Backstage Configuration Files
---
## File Format
+9 -3
View File
@@ -1,4 +1,10 @@
# Contributing to Storybook
---
id: contributing-to-storybook
title: Contributing to Storybook
---
You find our storybook at
[http://backstage.io/storybook](http://backstage.io/storybook)
## Creating a new Story
@@ -26,7 +32,7 @@ core
Go to `packages/storybook`, run `yarn install` and install the dependencies,
then run the following on your command line: `yarn start`
![](running-storybook.png)
![](../assets/dls/running-storybook.png)
_You should see a log like the image above._
@@ -34,4 +40,4 @@ If everything worked out, your server will be running on **port 6006**, go to
your browser and navigate to `http://localhost:6006/`. You should be able to
navigate and see the Storybook page.
![](storybook-page.png)
![](../assets/dls/storybook-page.png)
+12 -8
View File
@@ -1,4 +1,9 @@
![header](designheader.png)
---
id: design
title: Design
---
![header](../assets/dls/designheader-updated.png)
Much like Backstage Open Source, this is a _living_ document! We'll keep this
updated as we evolve our practices!
@@ -60,7 +65,7 @@ that is shaped by user experience and user interface decisions made by our
Backstage Design Team. Also note, we encourage you to take the core experience
weve crafted and add custom theming to better represent your organization!
![dls](DLS.png)
![dls](../assets/dls/DLS.png)
## ✅ Our Priorities
@@ -106,18 +111,17 @@ picked up by our team as something to be added to our design system.
## ✏️ Resources
**[Storybook](http://storybook.backstage.io/)** - where you can view our
**[Storybook](http://backstage.io/storybook)** - where you can view our
components. If youd like to help build up our design system, you can also add
components weve designed to the Storybook as well.
**[Figma](https://www.figma.com/@backstage)** - we're stoked to be using Figma
Community to share our design assets. You can duplicate our UI Kit
and design your own plugin for Backstage.
**[Discord](https://discord.gg/EBHEGzX)** - all design questions should be
directed to the _#design_ channel.
**Documentation**
- Patterns (stay tuned)
- Figma files/libraries (stay tuned)
## 🔮 Future
### Contributions from designers
+7 -1
View File
@@ -1 +1,7 @@
We have a [Figma component library](https://www.figma.com/@backstage) that you can use to build your own plugins for Backstage.
---
id: figma
title: Figma
---
We have a [Figma component library](https://www.figma.com/@backstage) that you
can use to build your own plugins for Backstage.
+6
View File
@@ -0,0 +1,6 @@
---
id: software-catalog-api
title: API
---
## TODO
@@ -0,0 +1,58 @@
---
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`, `API` and `Location`. 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, Location, Template]
locations:
- type: github
target: https://github.com/org/example/blob/master/org-data.yaml
rules:
- allow: [Group]
```
We are able to add entities of kind `Component`, `API`, `Location`, or
`Template` 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 the default kinds if you want
those to still be allowed.
The following configuration will reject any kind of entities from being added to
the catalog:
```yaml
catalog:
rules: []
```
@@ -1,4 +1,8 @@
# Descriptor Format of Catalog Entities
---
id: descriptor-format
title: Descriptor Format of Catalog Entities
sidebar_label: YAML File Format
---
This section describes the default data shape and semantics of catalog entities.
@@ -14,6 +18,8 @@ humans. However, the structure and semantics is the same in both cases.
- [Common to All Kinds: The Envelope](#common-to-all-kinds-the-envelope)
- [Common to All Kinds: The Metadata](#common-to-all-kinds-the-metadata)
- [Kind: Component](#kind-component)
- [Kind: Template](#kind-template)
- [Kind: API](#kind-api)
## Overall Shape Of An Entity
@@ -36,6 +42,7 @@ software catalog API.
"labels": {
"system": "public-websites"
},
"tags": ["java"],
"name": "artist-web",
"uid": "2152f463-549d-4d8d-a94d-ce2b7676c6e2"
},
@@ -60,6 +67,8 @@ metadata:
annotations:
example.com/service-discovery: artistweb
circleci.com/project-slug: gh/example-org/artist-website
tags:
- java
spec:
type: website
lifecycle: production
@@ -80,7 +89,7 @@ The root envelope object has the following structure.
### `apiVersion` and `kind` [required]
The `kind` is the high level entity type being described.
[ADR005](/docs/architecture-decisions/adr005-catalog-core-entities.md) describes
[ADR005](../../architecture-decisions/adr005-catalog-core-entities.md) describes
a number of core kinds that plugins can know of and understand, but an
organization using Backstage is free to also add entities of other kinds to the
catalog.
@@ -226,6 +235,20 @@ The `backstage.io/` prefix is reserved for use by Backstage core components.
Values can be of any length, but are limited to being strings.
### `tags` [optional]
A list of single-valued strings, for example to classify catalog entities in
various ways. This is different to the labels in metadata, as labels are
key-value pairs.
The values are user defined, for example the programming language used for the
component, like `java` or `go`.
This field is optional, and currently has no special semantics.
Each tag must be sequences of `[a-zA-Z0-9]` separated by `-`, at most 63
characters in total.
## Kind: Component
Describes the following entity kind:
@@ -252,6 +275,8 @@ spec:
type: website
lifecycle: production
owner: artist-relations@example.com
implementsApis:
- artist-api
```
In addition to the [common envelope metadata](#common-to-all-kinds-the-metadata)
@@ -312,14 +337,22 @@ Apart from being a string, the software catalog leaves the format of this field
open to implementers to choose. Most commonly, it is set to the ID or email of a
group of people in an organizational structure.
### `spec.implementsApis` [optional]
Links APIs that are implemented by the component, e.g. `artist-api`. This field
is optional.
The software catalog expects a list of one or more strings that references the
names of other entities of the `kind` `API`.
## Kind: Template
Describes the following entity kind:
| Field | Value |
| -------------------- | ----------------------- |
| `apiVersion` | `backstage.io/v1alpha1` |
| `Kind: Templatekind` | `Template` |
| Field | Value |
| ------------ | ----------------------- |
| `apiVersion` | `backstage.io/v1alpha1` |
| `kind` | `Template` |
A Template describes a skeleton for use with the Scaffolder. It is used for
describing what templating library is supported, and also for documenting the
@@ -337,8 +370,8 @@ metadata:
description:
Next.js application skeleton for creating isomorphic web applications.
tags:
- Recommended
- React
- recommended
- react
spec:
owner: web@example.com
templater: cookiecutter
@@ -422,3 +455,76 @@ specify relative to the `template.yaml` definition.
This is also particularly useful when you have multiple template definitions in
the same repository but only a single `template.yaml` registered in backstage.
## Kind: API
Describes the following entity kind:
| Field | Value |
| ------------ | ----------------------- |
| `apiVersion` | `backstage.io/v1alpha1` |
| `kind` | `API` |
An API describes an interface that can be exposed by a component. The API can be
defined in different formats, like [OpenAPI](https://swagger.io/specification/),
[AsyncAPI](https://www.asyncapi.com/docs/specifications/latest/),
[gRPC](https://developers.google.com/protocol-buffers), or other formats.
Descriptor files for this kind may look as follows.
```yaml
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: artist-api
description: Retrieve artist details
spec:
type: openapi
definition: |
openapi: "3.0.0"
info:
version: 1.0.0
title: Artist API
license:
name: MIT
servers:
- url: http://artist.spotify.net/v1
paths:
/artists:
get:
summary: List all artists
...
```
In addition to the [common envelope metadata](#common-to-all-kinds-the-metadata)
shape, this kind has the following structure.
### `apiVersion` and `kind` [required]
Exactly equal to `backstage.io/v1alpha1` and `API`, respectively.
### `spec.type` [required]
The type of the API definition as a string, e.g. `openapi`. This field is
required.
The software catalog accepts any type value, but an organisation should take
great care to establish a proper taxonomy for these. Tools including Backstage
itself may read this field and behave differently depending on its value. For
example, an OpenAPI type API may be displayed using an OpenAPI viewer tooling in
the Backstage interface.
The current set of well-known and common values for this field is:
- `openapi` - An API definition in YAML or JSON format based on the
[OpenAPI](https://swagger.io/specification/) version 2 or version 3 spec.
- `asyncapi` - An API definition based on the
[AsyncAPI](https://www.asyncapi.com/docs/specifications/latest/) spec.
- `grpc` - An API definition based on
[Protocol Buffers](https://developers.google.com/protocol-buffers) to use with
[gRPC](https://grpc.io/).
### `spec.definition` [required]
The definition of the API, based on the format defined by `spec.type`. This
field is required.
@@ -0,0 +1,42 @@
---
id: extending-the-model
title: Extending the model
---
Backstage natively supports tracking of the following component
[`type`](descriptor-format.md)'s:
- Services
- Websites
- Libraries
- Documentation
- Other
![](../../assets/software-catalog/bsc-extend.png)
Since these types are likely not the only kind of software you will want to
track in Backstage, it is possible to
It is possible to add your own software types that fits your organization's data
model. Inside Spotify our model has grown significantly over the years, and now
includes ML models, Apps, data pipelines and many more.
## Adding a new type
TODO: Describe what changes are needed to add a new type that shows up in the
catalog.
## The Other type
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 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)
only makes sense for Websites. The more specific you can be in how you model
your software, the easier it is to provide plugins that are contextual.
@@ -0,0 +1,11 @@
---
id: external-integrations
title: External integrations
---
Backstage natively supports storing software components in
[metadata YAML files](descriptor-format.md). However, companies that already
have an existing system for keeping track of software and its owners can
integrate such systems with Backstage.
TODO: Describe the API contract.
+128 -13
View File
@@ -1,4 +1,8 @@
# Backstage Service Catalog (alpha)
---
id: software-catalog-overview
title: Backstage Service Catalog (alpha)
sidebar_label: Backstage Service Catalog
---
## What is a Service Catalog?
@@ -6,21 +10,132 @@ The Backstage Service Catalog — actually, a software catalog, since it include
more than just services — is a centralized system that keeps track of ownership
and metadata for all the software in your ecosystem (services, websites,
libraries, data pipelines, etc). The catalog is built around the concept of
[metadata yaml files](../../architecture-decisions/adr002-default-catalog-file-format.md#format)
stored together with the code, which are then harvested and visualized in
Backstage.
[metadata YAML files](descriptor-format.md) stored together with the code, which
are then harvested and visualized in Backstage.
![service-catalog](https://backstage.io/blog/assets/6/header.png)
We have also found that the service catalog is a great way to organise the
infrastructure tools you use to manage the software as well. This is how
Backstage creates one developer portal for all your tools. Rather than asking
teams to jump between different infrastructure UIs (and incurring additional
cognitive overhead each time they make a context switch), most of these tools
can be organised around the entities in the catalog.
## How it works
## Using the Service Catalog
Backstage and the Backstage Service Catalog makes it easy for one team to manage
10 services — and makes it possible for your company to manage thousands of
them.
TODO
More specifically, the Service Catalog enables two main use-cases:
![](service-catalog-home.png)
1. Helping teams manage and maintain the software they own. Teams get a uniform
view of all their software; services, libraries, websites, ML models — you
name it, Backstage knows all about it.
2. Makes all the software in your company, and who owns it, discoverable. No
more orphan software hiding in the dark corners of your software ecosystem.
## Getting Started
The Software Catalog is available to browse on the start page at `/`. If you've
followed [Installing in your Backstage App](./installation.md) in your separate
App or [Getting Started with Backstage](../../getting-started) for this repo,
you should be able to browse the catalog at `http://localhost:3000`.
![](../../assets/software-catalog/service-catalog-home.png)
## Adding components to the catalog
The source of truth for the components in your service catalog are
[metadata YAML files](descriptor-format.md) stored in source control (GitHub,
GitHub Enterprise, GitLab, ...).
There are 3 ways to add components to the catalog:
1. Manually register components
2. Creating new components through Backstage
3. Integrating with and [external source](external-integrations.md)
### Manually register components
Users can register new components by going to `/create` and clicking the
**REGISTER EXISTING COMPONENT** button:
![](../../assets/software-catalog/bsc-register-1.png)
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)._
![](../../assets/software-catalog/bsc-register-2.png)
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
offering, for example) it is still useful to create components for tracking
ownership.
### Creating new components through Backstage
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.
![](../../assets/software-catalog/bsc-edit.png)
Once the change has been merged, Backstage will automatically show the updated
metadata in the service catalog after a short while.
## Finding software in the catalog
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
company's software ecosystem. Basic inline _search_ and _column filtering_ makes
it easy to browse a big set of components.
![](../../assets/software-catalog/bsc-search.png)
## Starring components
For easy and quick access to components you visit frequently, Backstage supports
_starring_ of components:
![](../../assets/software-catalog/bsc-starred.png)
## Integrated tooling through plugins
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 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.
![tools](https://backstage.io/blog/assets/20-05-20/tabs.png)
The Backstage platform can be customized by incorporating
[existing open source plugins](https://github.com/spotify/backstage/tree/master/plugins),
or by [building your own](../../plugins/index.md).
## Links
- [[Blog post] Backstage Service Catalog released in alpha](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)
@@ -0,0 +1,193 @@
# Installing in your Backstage App
The catalog plugin comes in two packages, `@backstage/plugin-catalog` and
`@backstage/plugin-catalog-backend`. Each has their own installation steps,
outlined below.
## Installing @backstage/plugin-catalog
> **Note that if you used `npx @backstage/create-app`, the plugin may already be
> present**
The catalog frontend plugin should be installed in your `app` package, which is
created as a part of `@backstage/create-app`. To install the package, run:
```bash
cd packages/app
yarn add @backstage/plugin-catalog
```
Make sure the version of `@backstage/plugin-catalog` matches the version of
other `@backstage` packages. You can update it in `packages/app/package.json` if
it doesn't.
### Adding the Plugin to your `packages/app`
Add the following entry to the head of your `packages/app/src/plugins.ts`:
```ts
export { plugin as CatalogPlugin } from '@backstage/plugin-catalog';
```
Add the following to your `packages/app/src/apis.ts`:
```ts
import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog';
// Inside the ApiRegistry builder function ...
builder.add(
catalogApiRef,
new CatalogClient({
apiOrigin: backendUrl,
basePath: '/catalog',
}),
);
```
Where `backendUrl` is the `backend.baseUrl` from config, i.e.
`const backendUrl = config.getString('backend.baseUrl')`.
The catalog components depend on a number of other
[Utility APIs](../../api/utility-apis.md) to function, including at least the
`ErrorApi` and `StorageApi`. You can find an example of how to install these in
your app
[here](https://github.com/spotify/backstage/blob/61c3a7e5b750dc7c059ef16b188594d31b2c04c2/packages/app/src/apis.ts#L80).
## Gotchas that we will fix
Since the catalog plugin currently ships with a sentry plugin `InfoCard`
installed by default, you'll need to set `sentry.organization` in your
`app-yaml.yaml`. For example:
```yaml
sentry:
organization: Acme Corporation
```
If you've created an app with an older version of `@backstage/create-app` or
`@backstage/cli create-app`, be sure to remove the Welcome plugin from the app,
as that will conflict with the catalog routes.
## Installing @backstage/plugin-catalog-backend
> **Note that if you used `npx @backstage/create-app`, the plugin may already be
> present**
The catalog backend should be installed in your `backend` package, which is
created as a part of `@backstage/create-app`. To install the package, run:
```bash
cd packages/backend
yarn add @backstage/plugin-catalog-backend
```
Make sure the version of `@backstage/plugin-catalog-backend` matches the version
of other `@backstage` packages. You can update it in
`packages/backend/package.json` if it doesn't.
### Adding the Plugin to your `packages/backend`
You'll need to add the plugin to the `backend`'s router. You can do this by
creating a file called `packages/backend/src/plugins/catalog.ts` with the
following contents to get you up and running quickly.
```ts
import {
createRouter,
DatabaseEntitiesCatalog,
DatabaseLocationsCatalog,
DatabaseManager,
HigherOrderOperations,
LocationReaders,
runPeriodically,
} from '@backstage/plugin-catalog-backend';
import { PluginEnvironment } from '../types';
import { useHotCleanup } from '@backstage/backend-common';
export default async function createPlugin({
logger,
database,
}: PluginEnvironment) {
const locationReader = new LocationReaders(logger);
const db = await DatabaseManager.createDatabase(database, { logger });
const entitiesCatalog = new DatabaseEntitiesCatalog(db);
const locationsCatalog = new DatabaseLocationsCatalog(db);
const higherOrderOperation = new HigherOrderOperations(
entitiesCatalog,
locationsCatalog,
locationReader,
logger,
);
useHotCleanup(
module,
runPeriodically(() => higherOrderOperation.refreshAllLocations(), 10000),
);
return await createRouter({
entitiesCatalog,
locationsCatalog,
higherOrderOperation,
logger,
});
}
```
Once the `catalog.ts` router setup file is in place, add the router to
`packages/backend/src/index.ts`:
```ts
import catalog from './plugins/catalog';
const catalogEnv = useHotMemoize(module, () => createEnv('catalog'));
const service = createServiceBuilder(module)
.loadConfig(configReader)
/** several different routers */
.addRouter('/catalog', await catalog(catalogEnv));
```
### Adding Entries to the Catalog
At this point the catalog backend is installed in your backend package, but you
will not have any entities loaded.
To get up and running and try out some templates quickly, you can add some of
our example templates through static configuration. Add the following to the
`catalog.locations` section in your `app-config.yaml`:
```yaml
catalog:
locations:
# Backstage Example Component
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-order-component.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/podcast-api-component.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/queue-proxy-component.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/searcher-component.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-lib-component.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/www-artist-component.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/shuffle-api-component.yaml
```
### Running the Backend
Finally, start up the backend with the new configuration:
```bash
cd packages/backend
yarn start
```
If you've also set up the frontend plugin, so you should be ready to go browse
the catalog at [localhost:3000](http://localhost:3000) now!
@@ -0,0 +1,116 @@
---
id: system-model
title: System Model
---
We believe that a strong shared understanding and terminology around software
and resources leads to a better Backstage experience.
_This description originates from
[this RFC](https://github.com/spotify/backstage/issues/390). Note that some of
the concepts are not yet supported in Backstage._
## Core Entities
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
![](../../assets/software-catalog/software-model-core-entities.png)
### Component
A component is a piece of software, for example a mobile feature, web site,
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.
### 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.
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.
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
consumers), or private (only available within their system). As public APIs are
going to be the primary way interaction between components, Backstage supports
documenting, indexing and searching all APIs so we can browse them as
developers.
### Resource
Resources are the infrastructure a component needs to operate at runtime, like
BigTable databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together
with components and systems will better allow us to visualize resource
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
### System
With increasing complexity in software, systems form an important abstraction
level to help us reason about software ecosystems. Systems are a useful concept
in that they allow us to ignore the implementation details of a certain
functionality for consumers, while allowing the owning team to make changes as
they see fit (leading to low coupling).
A system, in this sense, is a collection of resources and components that
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).
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
bounded context.
For example, it would make sense if the different systems in the “Payments”
domain would come with some documentation on how to accept payments for a new
product or use-case, share the same entity types in their APIs, and integrate
well with each other. Other domains could be “Content Ingestion”, “Ads” or
“Search”.
## Current status
Backstage currently supports Components and APIs.
## Links
- [Original RFC](https://github.com/spotify/backstage/issues/390)
- [YAML file format](../../architecture-decisions/adr002-default-catalog-file-format.md)
@@ -1,4 +1,7 @@
# Adding your own Templates
---
id: adding-templates
title: Adding your own Templates
---
Templates are stored in the **Service Catalog** under a kind `Template`. The
minimum that the a template skeleton needs is a `template.yaml` but it would be
@@ -15,11 +18,12 @@ metadata:
# title of the template
title: React SSR Template
# a description of the template
description: Next.js application skeleton for creating isomorphic web applications.
description:
Next.js application skeleton for creating isomorphic web applications.
# some tags to display in the frontend
tags:
- Recommended
- React
- recommended
- react
spec:
# which templater key to use in the templaters builder
templater: cookiecutter
@@ -39,7 +43,7 @@ spec:
type: string
description: Unique name of the component
description:
title: Description
title: Description
type: string
description: Description of the component
```
@@ -50,11 +54,34 @@ contains more information about the required fields.
Once we have a `template.yaml` ready, we can then add it to the service catalog
for use by the scaffolder.
Currently the catalog supports loading definitions from Github + Local Files. To
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
You can add the template files to the catalog through
[static location configuration](../software-catalog/configuration.md#static-location-configuration),
for example
```yaml
catalog:
locations:
- type: github
target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml
rules:
- allow: [Template]
```
Templates can also be added by posting the to the catalog directly. Note that if
you're doing this, you need to configure the catalog to allow template entities
to be ingested from any source, for example:
```yaml
catalog:
rules:
- allow: [Component, API, Template]
```
For loading from a file, the following command should work when the backend is
running:
```sh
@@ -65,7 +92,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 \
@@ -79,7 +106,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
@@ -1,4 +1,7 @@
# Create your own Preparer
---
id: extending-preparer
title: Create your own Preparer
---
Preparers are responsible for reading the location of the definition of a
[Template Entity](../../software-catalog/descriptor-format.md#kind-template) and
@@ -51,7 +54,7 @@ The `protocol` is set on the
when added to the service catalog. You can see more about this `PreparerKey`
here in [Register your own template](../adding-templates.md)
**note:** Currently the catalog supports loading definitions from Github + Local
**note:** Currently the catalog supports loading definitions from GitHub + Local
Files, which translate into the two `PreparerKeys` `file` and `github`. 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.
@@ -1,11 +1,14 @@
# Create your own Publisher
---
id: extending-publisher
title: Create your own Publisher
---
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.
@@ -14,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
@@ -1,18 +1,21 @@
# Creating your own Templater
---
id: extending-templater
title: Creating your own Templater
---
Templaters are responsible for taking the directory path for the skeleton
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
@@ -45,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`
@@ -80,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!
@@ -113,8 +116,8 @@ metadata:
description:
Next.js application skeleton for creating isomorphic web applications.
tags:
- Recommended
- React
- recommended
- react
spec:
owner: web@example.com
templater: handlebars
@@ -135,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
@@ -1,12 +1,15 @@
## Extending the Scaffolder
---
id: extending-index
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.
@@ -22,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
@@ -35,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,
@@ -47,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
@@ -75,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.
+27 -19
View File
@@ -1,19 +1,27 @@
# Software Templates
---
id: software-templates-index
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>
<source src="https://backstage.io/blog/assets/2020-08-05/feature.mp4" type="video/mp4">
</video>
### Getting Started
The Software Templates are available under `/create`, and if you've followed
[Getting Started with Backstage](../../getting-started), you should be able to
reach `http://localhost:3000/create`.
The Software Templates are available under `/create`. If you've followed
[Installing in your Backstage App](./installation.md) in your separate App or
[Getting Started with Backstage](../../getting-started) for this repo, you
should be able to reach `http://localhost:3000/create`.
You should get something that looks similar to this:
![Create Image](./assets/create.png)
![Create Image](../../assets/software-templates/create.png)
### Choose a template
@@ -22,38 +30,38 @@ page which may or may not look different for each template. Each template can
ask for different input variables, and they are then passed to the templater
internally.
![Enter some variables](./assets/template-picked.png)
![Enter some variables](../../assets/software-templates/template-picked.png)
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
non-existing github repository name in the format `organistaion/reponame`.
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`.
![Enter backstage vars](./assets/template-picked-2.png)
![Enter backstage vars](../../assets/software-templates/template-picked-2.png)
### Run!
Once you've entered values and confirmed, you'll then get a modal with live
progress of what is currently happening with the creation of your template.
![Templating Running](./assets/running.png)
![Templating Running](../../assets/software-templates/running.png)
It shouldn't take too long, and you'll have a success screen!
![Templating Complete](./assets/complete.png)
![Templating Complete](../../assets/software-templates/complete.png)
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.
![Templating failed](./assets/failed.png)
![Templating failed](../../assets/software-templates/failed.png)
### 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:
![Catalog](./assets/go-to-catalog.png)
![Catalog](../../assets/software-templates/go-to-catalog.png)
And then you'll also be able to see it in the Catalog View table
![Catalog](./assets/added-to-the-catalog-list.png)
![Catalog](../../assets/software-templates/added-to-the-catalog-list.png)
@@ -0,0 +1,194 @@
# Installing in your Backstage App
The scaffolder plugin comes in two packages, `@backstage/plugin-scaffolder` and
`@backstage/plugin-scaffolder-backend`. Each has their own installation steps,
outlined below.
The Scaffolder plugin also depends on the Software Catalog. Instructions for how
to set that up can be found [here](../software-catalog/installation.md).
## Installing @backstage/plugin-scaffolder
> **Note that if you used `npx @backstage/create-app`, the plugin may already be
> present**
The scaffolder frontend plugin should be installed in your `app` package, which
is created as a part of `@backstage/create-app`. To install the package, run:
```bash
cd packages/app
yarn add @backstage/plugin-scaffolder
```
Make sure the version of `@backstage/plugin-scaffolder` matches the version of
other `@backstage` packages. You can update it in `packages/app/package.json` if
it doesn't.
### Adding the Plugin to your `packages/app`
Add the following entry to the head of your `packages/app/src/plugins.ts`:
```ts
export { plugin as ScaffolderPlugin } from '@backstage/plugin-scaffolder';
```
Add the following to your `packages/app/src/apis.ts`:
```ts
import { scaffolderApiRef, ScaffolderApi } from '@backstage/plugin-scaffolder';
// Inside the ApiRegistry builder function ...
builder.add(
scaffolderApiRef,
new ScaffolderApi({
apiOrigin: backendUrl,
basePath: '/scaffolder/v1',
}),
);
```
Where `backendUrl` is the `backend.baseUrl` from config, i.e.
`const backendUrl = config.getString('backend.baseUrl')`.
This is all that is needed for the frontend part of the Scaffolder plugin to
work!
## Installing @backstage/plugin-scaffolder-backend
> **Note that if you used `npx @backstage/create-app`, the plugin may already be
> present**
The scaffolder backend should be installed in your `backend` package, which is
created as a part of `@backstage/create-app`. To install the package, run:
```bash
cd packages/backend
yarn add @backstage/plugin-scaffolder-backend
```
Make sure the version of `@backstage/plugin-scaffolder-backend` matches the
version of other `@backstage` packages. You can update it in
`packages/backend/package.json` if it doesn't.
### Adding the Plugin to your `packages/backend`
You'll need to add the plugin to the `backend`'s router. You can do this by
creating a file called `packages/backend/src/plugins/scaffolder.ts` with the
following contents to get you up and running quickly.
```ts
import {
CookieCutter,
createRouter,
FilePreparer,
GithubPreparer,
Preparers,
GithubPublisher,
CreateReactAppTemplater,
Templaters,
} from '@backstage/plugin-scaffolder-backend';
import { Octokit } from '@octokit/rest';
import type { PluginEnvironment } from '../types';
import Docker from 'dockerode';
export default async function createPlugin({ logger }: PluginEnvironment) {
const cookiecutterTemplater = new CookieCutter();
const craTemplater = new CreateReactAppTemplater();
const templaters = new Templaters();
// Register default templaters
templaters.register('cookiecutter', cookiecutterTemplater);
templaters.register('cra', craTemplater);
const filePreparer = new FilePreparer();
const githubPreparer = new GithubPreparer();
const preparers = new Preparers();
// Register default preparers
preparers.register('file', filePreparer);
preparers.register('github', githubPreparer);
// Create GitHub client with your access token from environment variables
const githubClient = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN });
const publisher = new GithubPublisher({ client: githubClient });
const dockerClient = new Docker();
return await createRouter({
preparers,
templaters,
publisher,
logger,
dockerClient,
});
}
```
Once the `scaffolder.ts` router setup file is in place, add the router to
`packages/backend/src/index.ts`:
```ts
import scaffolder from './plugins/scaffolder';
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
const service = createServiceBuilder(module)
.loadConfig(configReader)
/** several different routers */
.addRouter('/scaffolder', await scaffolder(scaffolderEnv));
```
### Adding Templates
At this point the scaffolder backend is installed in your backend package, but
you will not have any templates available to use. These need to be added to the
software catalog, as they are represented as entities of kind
[Template](../software-catalog/descriptor-format.md#kind-template). You can find
out more about adding templates [here](./adding-templates.md).
To get up and running and try out some templates quickly, you can add some of
our example templates through static configuration. Add the following to the
`catalog.locations` section in your `app-config.yaml`:
```yaml
catalog:
locations:
# Backstage Example Templates
- type: github
target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml
- type: github
target: https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml
- type: github
target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml
```
### Runtime Dependencies
For the scaffolder backend plugin to function, it needs a GitHub access token,
and access to a running Docker daemon. You can create a GitHub access token
[here](https://github.com/settings/tokens/new), select `repo` scope only. Full
docs on creating private GitHub access tokens is available
[here](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token).
Note that the need for private GitHub access tokens will be replaced with GitHub
Apps integration further down the line.
> **Right now it is only possible to scaffold repositories inside GitHub
> organizations, and not under personal accounts.**
The GitHub access token is passed along using the `GITHUB_ACCESS_TOKEN`
environment variable.
### Running the Backend
Finally, make sure you have a local Docker daemon running, and start up the
backend with the new configuration:
```bash
cd packages/backend
GITHUB_ACCESS_TOKEN=<token> yarn start
```
If you've also set up the frontend plugin, so you should be ready to go browse
the templates at [localhost:3000/create](http://localhost:3000/create) now!
+7 -3
View File
@@ -1,8 +1,13 @@
# TechDocs FAQ
---
id: faqs
title: TechDocs FAQ
sidebar_label: FAQ
---
This page answers frequently asked questions about [TechDocs](README.md).
_Got a question that you think others might be interested in knowing the answer to? Edit this file
_Got a question that you think others might be interested in knowing the answer
to? Edit this file
[here](https://github.com/spotify/backstage/edit/master/docs/features/techdocs/FAQ.md)._
## Technology
@@ -25,4 +30,3 @@ package is a MkDocs Plugin that works like a wrapper around multiple MkDocs
plugins (e.g.
[MkDocs Monorepo Plugin](https://github.com/spotify/mkdocs-monorepo-plugin)) as
well as a selection of Python Markdown extensions that TechDocs supports.
+23 -4
View File
@@ -1,4 +1,8 @@
# TechDocs Documentation
---
id: techdocs-overview
title: TechDocs Documentation
sidebar_label: Overview
---
## What is it?
@@ -16,9 +20,9 @@ Spotifys developer experience offering with 2,400+ documentation sites and
- A clear end-to-end docs-like-code solution. (_Coming soon in V.1_)
- A tightly coupled feedback loop with the developer workflow. (_Coming soon in
V.2_)
V.3_)
- A developer ecosystem for creating extensions. (_Coming soon in V.2_)
- A developer ecosystem for creating extensions. (_Coming soon in V.3_)
## Project roadmap
@@ -26,12 +30,15 @@ Spotifys developer experience offering with 2,400+ documentation sites and
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| [TechDocs V.0 ✅][v0] | Read docs in Backstage - Enable anyone to get a reader experience working in Backstage. [See V.0 Use Cases.](./#techdocs-v0) |
| [TechDocs V.1 🚧][v1] | TechDocs end to end (alpha) - Alpha of TechDocs that you can use end to end - and contribute to. [See V.1 Use Cases.](./#techdocs-v1) |
| [TechDocs V.2 🔮⌛][v2] | Widget Architecture - TechDocs widget architecture available, so the community can create their own customized features. |
| [TechDocs V.2 🔮⌛][v2] | Platform stability and compatibility improvements. [See V.2 Use Cases.](./#techdocs-v2) |
| TechDocs V.3 🔮⌛ | Widget Architecture - TechDocs widget architecture available, so the community can create their own customized features. |
[v0]: https://github.com/spotify/backstage/milestone/15
[v1]: https://github.com/spotify/backstage/milestone/16
[v2]: https://github.com/spotify/backstage/milestone/17
<!-- TODO: Add link to milestone for v3 -->
## Use Cases
#### TechDocs V.0
@@ -56,6 +63,14 @@ Spotifys developer experience offering with 2,400+ documentation sites and
#### TechDocs V.2
Platform stability and compatibility improvements
- As a user I can define the metadata generated for my documentation.
- As a user I will be able to browse metadata from within my documentation in
Backstage.
#### TechDocs V.3
more to come...
## Structure
@@ -86,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
![TechDocs Big Picture](../..//assets/techdocs/techdocs_big_picture.png)

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