@@ -5,3 +5,4 @@
|
||||
**/build/**
|
||||
**/.git/**
|
||||
**/public/**
|
||||
**/microsite/**
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
# The last matching pattern takes precedence.
|
||||
# https://help.github.com/articles/about-codeowners/
|
||||
|
||||
* @spotify/backstage-core
|
||||
/plugins/techdocs @spotify/techdocs-core
|
||||
/packages/techdocs-cli @spotify/techdocs-core
|
||||
* @spotify/backstage-core
|
||||
/docs/features/techdocs @spotify/techdocs-core
|
||||
/plugins/techdocs @spotify/techdocs-core
|
||||
/plugins/techdocs-backend @spotify/techdocs-core
|
||||
/packages/techdocs-cli @spotify/techdocs-core
|
||||
/packages/techdocs-container @spotify/techdocs-core
|
||||
/.github/workflows/techdocs.yml @spotify/techdocs-core
|
||||
/.github/workflows/techdocs-pypi.yml @spotify/techdocs-core
|
||||
|
||||
@@ -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,9 +96,3 @@ jobs:
|
||||
|
||||
- name: verify plugin template
|
||||
run: yarn lerna -- run diff -- --check
|
||||
|
||||
- name: bundle example app
|
||||
run: yarn bundle
|
||||
|
||||
- name: verify storybook
|
||||
run: yarn workspace storybook build-storybook
|
||||
@@ -1,49 +0,0 @@
|
||||
name: CLI Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/cli.yml'
|
||||
- 'packages/cli/**'
|
||||
- 'packages/core/**'
|
||||
- 'packages/core-api/**'
|
||||
- 'yarn.lock'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node-version: [12.x]
|
||||
|
||||
env:
|
||||
CI: true
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
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-
|
||||
- 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
|
||||
- run: yarn tsc
|
||||
- run: yarn build
|
||||
- name: verify app and plugin creation
|
||||
run: |
|
||||
sudo sysctl fs.inotify.max_user_watches=524288
|
||||
node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js
|
||||
@@ -1,12 +1,14 @@
|
||||
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:
|
||||
build:
|
||||
@@ -24,23 +26,37 @@ 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
|
||||
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
|
||||
@@ -0,0 +1,76 @@
|
||||
name: E2E Test Linux
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'microsite/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
ports:
|
||||
- 5432/tcp
|
||||
# needed because the postgres container does not provide a healthcheck
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node-version: [12.x]
|
||||
|
||||
env:
|
||||
CI: true
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
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: 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
|
||||
- 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
|
||||
@@ -0,0 +1,53 @@
|
||||
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: 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
|
||||
|
||||
|
||||
# Tests are broken on Windows, disabled for now
|
||||
# - name: test
|
||||
# run: yarn lerna -- run test
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Master Build TechDocs PyPI Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- '.github/workflows/techdocs-pypi.yml'
|
||||
- 'packages/techdocs-container/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python-version: [3.7]
|
||||
|
||||
steps:
|
||||
# Publish techdocs-core to PyPI
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@master
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Build Python distribution
|
||||
working-directory: ./packages/techdocs-container/techdocs-core
|
||||
run: |
|
||||
pip install wheel
|
||||
rm -rf dist
|
||||
python setup.py bdist_wheel sdist --formats gztar
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_KEY }}
|
||||
packages_dir: ./packages/techdocs-container/techdocs-core/dist
|
||||
@@ -33,12 +33,12 @@ jobs:
|
||||
push: false
|
||||
|
||||
# Lint Python code for techdocs-core package
|
||||
- name: prepare python environment
|
||||
- name: Prepare Python environment
|
||||
run: |
|
||||
python3 -m pip install --index-url https://pypi.org/simple/ setuptools
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install --index-url https://pypi.org/simple/ -r $TECHDOCS_CORE_PATH/requirements.txt
|
||||
|
||||
- name: lint techdocs-core package
|
||||
- name: Lint techdocs-core package
|
||||
run: |
|
||||
python3 -m black --check $TECHDOCS_CORE_PATH/src
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
.vscode/
|
||||
.vsls.json
|
||||
|
||||
# @spotify/web-script build output
|
||||
cjs/
|
||||
esm/
|
||||
types/
|
||||
build/
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
@@ -61,6 +55,9 @@ typings/
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Node version directives
|
||||
.nvmrc
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
@@ -93,6 +90,9 @@ typings/
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Microsite build output
|
||||
microsite/build
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
@@ -119,3 +119,9 @@ dist
|
||||
|
||||
# Temporary change files created by Vim
|
||||
*.swp
|
||||
|
||||
# MkDocs build output
|
||||
site
|
||||
|
||||
# Local configuration files
|
||||
*.local.yaml
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
| Organization | Contact | Description of Use |
|
||||
| ------------ | ------- | ------------------ |
|
||||
| [Spotify](https://www.spotify.com) |[@alund](https://github.com/alund)| Main interface towards all of Spotify's infrastructure and technical documentation.|
|
||||
| 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 |
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Backstage Changelog
|
||||
|
||||
This is a best-effort changelog where we manually collect breaking changes. It is not an exhaustive list of all changes or even features added.
|
||||
|
||||
If you encounter issues while upgrading to a newer version, don't hesitate to reach out on [Discord](https://discord.gg/EBHEGzX) or [open an issue](https://github.com/spotify/backstage/issues/new/choose)!
|
||||
|
||||
## Next Release
|
||||
|
||||
> Collect changes for the next release below
|
||||
|
||||
## v0.1.1-alpha.20
|
||||
|
||||
- Includes https://github.com/spotify/backstage/pull/2097 to resolve issues with create-plugin command.
|
||||
|
||||
## v0.1.1-alpha.19
|
||||
|
||||
### @backstage/create-app
|
||||
|
||||
- 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
|
||||
|
||||
- Fixed an issue with duplicated location logs. Applying the database migrations from this fix will clear the existing migration logs. [#1836](https://github.com/spotify/backstage/pull/1836)
|
||||
|
||||
### @backstage/auth-backend
|
||||
|
||||
This version fixes a breakage in CSP policies set by the auth backend. If you're facing trouble with auth in alpha.17, upgrade to alpha.18.
|
||||
|
||||
- OAuth redirect URLs no longer receive the `env` parameter, as it is now passed through state instead. This will likely require a reconfiguration of the OAuth app, where a redirect URL like `http://localhost:7000/auth/google/handler/frame?env=development` should now be configured as `http://localhost:7000/auth/google/handler/frame`. [#1812](https://github.com/spotify/backstage/pull/1812)
|
||||
|
||||
### @backstage/core
|
||||
|
||||
- `SignInPage` props have been changed to receive a list of provider objects instead of simple string identifiers for all but the `'guest'` and `'custom'` providers. This opens up for configuration of custom providers, but may break existing configurations. See [packages/app/src/App.tsx](https://github.com/spotify/backstage/blob/032ba401af36a760efdac41668d7000ccf09bc57/packages/app/src/App.tsx#L36) and [packages/app/src/identityProviders.ts](https://github.com/spotify/backstage/blob/032ba401af36a760efdac41668d7000ccf09bc57/packages/app/src/identityProviders.ts#L24) for how to bring back the existing providers. [#1816](https://github.com/spotify/backstage/pull/1816)
|
||||
|
||||
## v0.1.1-alpha.17
|
||||
|
||||
### @backstage/techdocs-backend
|
||||
|
||||
- The techdocs backend now requires more configuration to be supplied when creating the router. See [packages/backend/src/plugins/techdocs.ts](https://github.com/spotify/backstage/blob/0201fd9b4a52429519dd59e9184106ba69456deb/packages/backend/src/plugins/techdocs.ts#L42) for an example. [#1736](https://github.com/spotify/backstage/pull/1736)
|
||||
|
||||
### @backstage/cli
|
||||
|
||||
- The `create-app` command was moved out from the CLI to a standalone package. It's now invoked with `npx @backstage/create-app` instead. [#1745](https://github.com/spotify/backstage/pull/1745)
|
||||
@@ -1,4 +1,7 @@
|
||||
# Contributing
|
||||
---
|
||||
id: CONTRIBUTING
|
||||
title: Contributing
|
||||
---
|
||||
|
||||
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 can’t do it alone.
|
||||
|
||||
@@ -38,7 +41,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`).
|
||||
|
||||
@@ -54,12 +57,52 @@ If you are proposing a feature:
|
||||
- Remember that this is a volunteer-driven project, and that contributions
|
||||
are welcome :)
|
||||
|
||||
## Add your company to ADOPTERS
|
||||
|
||||
Have you started using Backstage? Adding your company to [ADOPTERS](ADOPTERS.md) really helps the project.
|
||||
|
||||
# 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).
|
||||
|
||||
# Coding Guidelines
|
||||
|
||||
All code is formatted with `prettier` using the configuration in the repo. If possible we recommend configuring your editor to format automatically, but you can also use the `yarn prettier --write <file>` command to format files.
|
||||
|
||||
If you're contributing to the backend or CLI tooling, be mindful of cross-platform support. [This](https://shapeshed.com/writing-cross-platform-node/) blog post is a good guide of what to keep in mind when writing cross-platform NodeJS.
|
||||
|
||||
Also be sure to skim through our [ADRs](https://github.com/spotify/backstage/tree/master/docs/architecture-decisions) to see if they cover what you're working on. In particular [ADR006: Avoid React.FC and React.SFC](https://github.com/spotify/backstage/blob/master/docs/architecture-decisions/adr006-avoid-react-fc.md) is one to look out for.
|
||||
|
||||
# Code of Conduct
|
||||
|
||||
This project adheres to the [Spotify FOSS Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# [Backstage](https://backstage.io)
|
||||
|
||||
@@ -10,105 +10,71 @@
|
||||
|
||||
## What is Backstage?
|
||||
|
||||
[Backstage](https://backstage.io/) is an open platform for building developer portals. It’s based on the developer portal we’ve 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 organization’s 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_, it’s 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.
|
||||
|
||||

|
||||
|
||||
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 UI’s (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 organization’s 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 Backstage’s 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 can’t 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/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** \* - Terminates HTTPS and exposes any RESTful API to Plugins.
|
||||
- **identity** - A backend service that holds your organisation's metadata.
|
||||
|
||||
_\* not yet released_
|
||||
|
||||
## 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/README.md) guide to learn more about how to extend the functionality with Plugins.
|
||||
### 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
|
||||
|
||||
- [Getting Started](docs/getting-started/README.md)
|
||||
- [Create a Backstage App](docs/create-an-app.md)
|
||||
- [Architecture](docs/architecture-terminology.md) ([Decisions](docs/architecture-decisions))
|
||||
- [API references](docs/reference/README.md)
|
||||
- [Designing for Backstage](docs/design.md)
|
||||
- [Storybook - UI components](http://storybook.backstage.io)
|
||||
- [Contributing to Storybook](docs/getting-started/contributing-to-storybook.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
We would love your help in building Backstage! See [CONTRIBUTING](CONTRIBUTING.md) for more information.
|
||||
- [Main documentation](https://backstage.io/docs/overview/what-is-backstage)
|
||||
- [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
|
||||
- [Newsletter](https://mailchi.mp/spotify/backstage-community)
|
||||
- Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️
|
||||
|
||||
@@ -4,14 +4,146 @@ 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:'
|
||||
|
||||
proxy:
|
||||
'/circleci/api':
|
||||
target: 'https://circleci.com/api/v1.1'
|
||||
changeOrigin: true
|
||||
pathRewrite:
|
||||
'^/proxy/circleci/api/': '/'
|
||||
'/jenkins/api':
|
||||
target: 'http://localhost:8080'
|
||||
changeOrigin: true
|
||||
headers:
|
||||
Authorization:
|
||||
$secret:
|
||||
env: JENKINS_BASIC_AUTH_HEADER
|
||||
pathRewrite:
|
||||
'^/proxy/jenkins/api/': '/'
|
||||
|
||||
organization:
|
||||
name: Spotify
|
||||
|
||||
techdocs:
|
||||
storageUrl: https://techdocs-mock-sites.storage.googleapis.com
|
||||
storageUrl: http://localhost:7000/techdocs/static/docs
|
||||
|
||||
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
|
||||
|
||||
catalog:
|
||||
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
|
||||
|
||||
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:
|
||||
env: AUTH_GITLAB_CLIENT_ID
|
||||
clientSecret:
|
||||
$secret:
|
||||
env: AUTH_GITLAB_CLIENT_SECRET
|
||||
audience:
|
||||
$secret:
|
||||
env: GITLAB_BASE_URL
|
||||
# saml:
|
||||
# development:
|
||||
# entryPoint: "http://localhost:7001/"
|
||||
# issuer: "passport-saml"
|
||||
okta:
|
||||
development:
|
||||
clientId:
|
||||
$secret:
|
||||
env: AUTH_OKTA_CLIENT_ID
|
||||
clientSecret:
|
||||
$secret:
|
||||
env: AUTH_OKTA_CLIENT_SECRET
|
||||
audience:
|
||||
$secret:
|
||||
env: AUTH_OKTA_AUDIENCE
|
||||
oauth2:
|
||||
development:
|
||||
clientId:
|
||||
$secret:
|
||||
env: AUTH_OAUTH2_CLIENT_ID
|
||||
clientSecret:
|
||||
$secret:
|
||||
env: AUTH_OAUTH2_CLIENT_SECRET
|
||||
authorizationUrl:
|
||||
$secret:
|
||||
env: AUTH_OAUTH2_AUTH_URL
|
||||
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:
|
||||
appOrigin: 'http://localhost:3000/'
|
||||
secure: false
|
||||
clientId:
|
||||
$secret:
|
||||
env: AUTH_MICROSOFT_CLIENT_ID
|
||||
clientSecret:
|
||||
$secret:
|
||||
env: AUTH_MICROSOFT_CLIENT_SECRET
|
||||
tenantId:
|
||||
$secret:
|
||||
env: AUTH_MICROSOFT_TENANT_ID
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage
|
||||
description: |
|
||||
Backstage is an open-source developer portal that puts the developer experience first.
|
||||
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
|
||||
lifecycle: experimental
|
||||
@@ -0,0 +1,16 @@
|
||||
# Make sure that before you
|
||||
# run the docker-compose that you have run
|
||||
# $ yarn docker-build:all
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
frontend:
|
||||
image: 'spotify/backstage:latest'
|
||||
ports:
|
||||
- '3000:80'
|
||||
backend:
|
||||
image: 'example-backend:latest'
|
||||
ports:
|
||||
- '7000:7000'
|
||||
environment:
|
||||
NODE_ENV: development
|
||||
@@ -11,6 +11,10 @@ server {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location /healthcheck {
|
||||
return 204;
|
||||
}
|
||||
|
||||
#error_page 404 /404.html;
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
|
||||
@@ -16,13 +16,16 @@ function inject_config() {
|
||||
with_entries(select(.key | startswith("APP_CONFIG_")) | .key |= sub("APP_CONFIG_"; "")) |
|
||||
to_entries |
|
||||
reduce .[] as $item (
|
||||
{}; setpath($item.key | split("_"); $item.value | fromjson)
|
||||
{}; setpath($item.key | split("_"); $item.value | try fromjson catch $item.value)
|
||||
)')"
|
||||
|
||||
>&2 echo "Runtime app config: $config"
|
||||
|
||||
local main_js
|
||||
main_js="$(grep -l __APP_INJECTED_RUNTIME_CONFIG__ /usr/share/nginx/html/*.chunk.js)"
|
||||
if ! main_js="$(grep -l __APP_INJECTED_RUNTIME_CONFIG__ /usr/share/nginx/html/static/*.js)"; then
|
||||
echo "Runtime config already written"
|
||||
return
|
||||
fi
|
||||
echo "Writing runtime config to ${main_js}"
|
||||
|
||||
# escape ' and " twice, for both sed and json
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
# Documentation
|
||||
|
||||
Check out <https://backstage.io> or see the table of contents below.
|
||||
|
||||
- [Architecture and Terminology](architecture-terminology.md)
|
||||
- [Getting Started](getting-started/README.md)
|
||||
- [References](reference/README.md)
|
||||
- [Publishing](publishing.md)
|
||||
- [Designing for Backstage](design.md)
|
||||
- [How to Add an Auth Provider](auth/add-auth-provider.md)
|
||||
The Backstage documentation is available at https://backstage.io/docs
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: backend
|
||||
title: Backend
|
||||
---
|
||||
|
||||
## TODO
|
||||
@@ -1,4 +1,7 @@
|
||||
# Utility APIs
|
||||
---
|
||||
id: utility-apis
|
||||
title: Utility APIs
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
@@ -19,7 +22,8 @@ during their entire life cycle.
|
||||
Each Utility API is tied to an `ApiRef` instance, which is a global singleton
|
||||
object without any additional state or functionality, its only purpose is to
|
||||
reference Utility APIs. `ApiRef`s are create using `createApiRef`, which is
|
||||
exported by `@backstage/core`. There are many predefined Utility APIs defined in
|
||||
exported by `@backstage/core`. There are many
|
||||
[predefined Utility APIs](../reference/utility-apis/README.md) defined in
|
||||
`@backstage/core`, and they're all exported with a name of the pattern
|
||||
`*ApiRef`, for example `errorApiRef`.
|
||||
|
||||
@@ -67,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
|
||||
});
|
||||
```
|
||||
@@ -152,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,6 +1,12 @@
|
||||
## Date: 2020-04-26
|
||||
---
|
||||
id: adrs-adr001
|
||||
title: ADR001: Architecture Decision Record (ADR) log
|
||||
sidebar_label: ADR001
|
||||
---
|
||||
|
||||
## Title: Architecture Decision Record (ADR) log
|
||||
| Created | Status |
|
||||
| ---------- | ------ |
|
||||
| 2020-04-26 | Open |
|
||||
|
||||
## Decision: A decision was made to store ADRs in a log in the project repository
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||

|
||||

|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: adrs-adr007
|
||||
title: ADR007: Use MSW to mock http requests
|
||||
sidebar_label: ADR007
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Network request mocking can be a total pain sometimes, in all different types of
|
||||
tests, unit tests to e2e tests always have their own implementation of mocking
|
||||
these requests. There's been traction in the outer community towards using this
|
||||
library to mock network requests by using an express style declaration for
|
||||
routes. react-testing-library suggests using this library instead of mocking
|
||||
fetch directly wether this be in a browser or in node.
|
||||
|
||||
https://github.com/mswjs/msw
|
||||
|
||||
## Decision
|
||||
|
||||
Moving forward, we have decided that any `fetch` or `XMLHTTPRequest` that
|
||||
happens, should be mocked by using `msw`.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```ts
|
||||
import { setupWorker, rest } from 'msw';
|
||||
|
||||
const worker = setupWorker(
|
||||
rest.get('*/user/:userId', (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json({
|
||||
firstName: 'John',
|
||||
lastName: 'Maverick',
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
// Start the Mock Service Worker
|
||||
worker.start();
|
||||
```
|
||||
|
||||
and in a more real life scenario, taken from
|
||||
[CatalogClient.test.ts](https://github.com/spotify/backstage/blob/f3245c4f8f0b6b2625c4a6d5d50161b612fb4757/plugins/catalog/src/api/CatalogClient.test.ts)
|
||||
|
||||
```ts
|
||||
beforeEach(() => {
|
||||
server.use(
|
||||
rest.get(`${mockApiOrigin}${mockBasePath}/entities`, (_, res, ctx) => {
|
||||
return res(ctx.json(defaultResponse));
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should entities from correct endpoint', async () => {
|
||||
const entities = await client.getEntities();
|
||||
expect(entities).toEqual(defaultResponse);
|
||||
});
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
- A little more code to write
|
||||
- Gradually will replace the codebase with `msw`
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
id: adrs-adr008
|
||||
title: ADR008: Default Catalog File Name
|
||||
sidebar_label: ADR008
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
While the spec for the catalog file format is well described in
|
||||
[ADR002](./adr002-default-catalog-file-format.md), guidance was note provided as
|
||||
to the name of the catalog file.
|
||||
|
||||
Following discussion in
|
||||
[Issue 1822](https://github.com/spotify/backstage/pull/1822#pullrequestreview-461253670),
|
||||
a decision was made.
|
||||
|
||||
## Name
|
||||
|
||||
The catalog file should be named
|
||||
|
||||
```shell
|
||||
catalog-info.yaml
|
||||
```
|
||||
|
||||
This name is a default, **not a requirement**. The catalog file will work with
|
||||
Backstage irregardless of its name.
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
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
|
||||
[this blog post](https://engineering.atspotify.com/2020/04/14/when-should-i-write-an-architecture-decision-record/).
|
||||
|
||||
Records are never deleted but can be marked as superseded by new decisions or
|
||||
deprecated.
|
||||
|
||||
Records should be stored under the `architecture-decisions` directory.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Creating an ADR
|
||||
|
||||
- Copy `0000-template.md` to `docs/architecture-decisions/0000-my-decision.md`
|
||||
(my-decision should be descriptive. Do not assign an ADR number.)
|
||||
- Fill in the ADR following the guidelines in the template
|
||||
- Submit a pull request
|
||||
- Address and integrate feedback from the community
|
||||
- Eventually, assign a number
|
||||
- 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
|
||||
|
||||
If an ADR supersedes an older ADR then the older ADR's status is changed to
|
||||
superseded by ADR-XXXX and links to the new ADR.
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 15 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 |
|
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 |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 414 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 384 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 746 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -1,4 +1,7 @@
|
||||
# Adding authentication providers
|
||||
---
|
||||
id: add-auth-provider
|
||||
title: Adding authentication providers
|
||||
---
|
||||
|
||||
## Passport
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
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 `OAuthProvider` 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. 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:
|
||||
appOrigin: "http://localhost:3000/"
|
||||
secure: false
|
||||
clientId:
|
||||
$secret:
|
||||
env: AUTH_GOOGLE_CLIENT_ID
|
||||
clientSecret:
|
||||
$secret:
|
||||
env: AUTH_GOOGLE_CLIENT_SECRET
|
||||
github:
|
||||
development:
|
||||
appOrigin: "http://localhost:3000/"
|
||||
secure: false
|
||||
clientId:
|
||||
$secret:
|
||||
env: AUTH_GITHUB_CLIENT_ID
|
||||
clientSecret:
|
||||
$secret:
|
||||
env: AUTH_GITHUB_CLIENT_SECRET
|
||||
enterpriseInstanceUrl:
|
||||
$secret:
|
||||
env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL
|
||||
gitlab:
|
||||
development:
|
||||
appOrigin: "http://localhost:3000/"
|
||||
secure: false
|
||||
clientId:
|
||||
$secret:
|
||||
...
|
||||
```
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### EnvironmentHandler
|
||||
|
||||
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 `EnvironmentHandler`.
|
||||
|
||||
An `EnvironmentHandler` takes an ID for each provider that it wraps, the
|
||||
handlers for each env the provider is supported in, and a function that, given a
|
||||
`Request` as argument, can extract the information about the env under which it
|
||||
should be processed.
|
||||
|
||||
Each provider exposes a factory function `createXProvider` (where X is the name
|
||||
of the provider) that takes the global config, env and other parameters and
|
||||
returns an `AuthProviderRouteHandlers` for each env, and an `envIdentifier`
|
||||
function to identify the env of a request.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: auth-backend
|
||||
title: Auth backend
|
||||
---
|
||||
|
||||
## TODO
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# User Authentication and Authorization in Backstage
|
||||
---
|
||||
id: index
|
||||
title: User Authentication and Authorization in Backstage
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
@@ -25,8 +28,9 @@ as possible to create new plugins, and an auth solution based on user-to-server
|
||||
OAuth helps in that regard.
|
||||
|
||||
The method with which frontend plugins request access to third party services is
|
||||
through [Utility APIs](../getting-started/utility-apis.md) for each service
|
||||
provider. For a full list of providers, see [TODO](#TODO).
|
||||
through [Utility APIs](../api/utility-apis.md) for each service provider. For a
|
||||
full list of providers, see the
|
||||
[Utility API References](../reference/utility-apis/README.md).
|
||||
|
||||
### Identity - WIP
|
||||
|
||||
@@ -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
|
||||
@@ -8,11 +11,12 @@ to various third party APIs.
|
||||
|
||||
There are occasions when the user wants to perform actions towards third party
|
||||
services that require authorization via OAuth. Backstage provides standardized
|
||||
[Utility APIs](../getting-started/utility-apis.md) such as the
|
||||
[GoogleAuthApi](../../packages/core-api/src/apis/definitions/auth.ts) for that
|
||||
use-case. Backstage also includes a set of implementations of these APIs that
|
||||
integrate with the [auth-backend](../../plugins/auth-backend) plugin to provide
|
||||
a popup-based OAuth flow.
|
||||
[Utility APIs](../api/utility-apis.md) such as the
|
||||
[GoogleAuthApi](https://github.com/spotify/backstage/blob/master/packages/core-api/src/apis/definitions/auth.ts)
|
||||
for that use-case. Backstage also includes a set of implementations of these
|
||||
APIs that integrate with the
|
||||
[auth-backend](https://github.com/spotify/backstage/tree/master/plugins/auth-backend)
|
||||
plugin to provide a popup-based OAuth flow.
|
||||
|
||||
## Background
|
||||
|
||||
@@ -51,8 +55,9 @@ easier to make authenticated requests inside a plugin.
|
||||
## OAuth Flow
|
||||
|
||||
The following describes the OAuth flow implemented by the
|
||||
[auth-backend](../../plugins/auth-backend) and
|
||||
[DefaultAuthConnector](../../packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts)
|
||||
[auth-backend](https://github.com/spotify/backstage/tree/master/plugins/auth-backend)
|
||||
and
|
||||
[DefaultAuthConnector](https://github.com/spotify/backstage/blob/master/packages/core-api/src/lib/AuthConnector/DefaultAuthConnector.ts)
|
||||
in `@backstage/core-api`.
|
||||
|
||||
Component and APIs can request Access or ID Tokens from any available Auth
|
||||
@@ -99,6 +104,8 @@ request an access token.
|
||||
|
||||
The following diagram visualizes the flow described in the previous section.
|
||||
|
||||

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

|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
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.
|
||||
|
||||
Meanwhile, document the config values that you are reading in your plugin
|
||||
README.
|
||||
|
||||
## Format
|
||||
|
||||
When defining configuration for your plugin, keep keys camelCased and stick to
|
||||
existing casing conventions such as `baseUrl`.
|
||||
|
||||
It is also usually best to prefer objects over arrays, as it makes it possible
|
||||
to override individual values using separate files or environment variables.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: index
|
||||
title: Static Configuration in Backstage
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Backstage ships with a flexible configuration system that provides a simple way
|
||||
to configure Backstage apps and plugins for both local development and
|
||||
production deployments. It helps get you up and running fast while adapting
|
||||
Backstage for your specific environment. It also serves as a tool for plugin
|
||||
authors to use to make it simple to pick up and install a plugin, while still
|
||||
allowing for customization.
|
||||
|
||||
## Supplying Configuration
|
||||
|
||||
Configuration is stored in `app-config.yaml` files, with support for suffixes
|
||||
such as `app-config.production.yaml` to override values for specific
|
||||
environments. The configuration files themselves contain plain YAML, but with
|
||||
support for loading in secrets from various sources using a `$secret` key.
|
||||
|
||||
It is also possible to supply configuration through environment variables, for
|
||||
example `APP_CONFIG_app_baseUrl=https://staging.example.com`. However these
|
||||
should be used sparingly, usually just for temporary overrides during
|
||||
development or small tweaks to be able to reuse deployment artifacts in
|
||||
different environments.
|
||||
|
||||
The configuration is shared between the frontend and backend, meaning that
|
||||
values that are common between the two only needs to be defined once. Such as
|
||||
the `backend.baseUrl`.
|
||||
|
||||
For more details, see [Writing Configuration](./writing.md).
|
||||
|
||||
## Reading Configuration
|
||||
|
||||
As a plugin developer, you likely end up wanting to define configuration that
|
||||
you want users of your plugin to supply, as well as reading that configuration
|
||||
in frontend and backend plugins. For more details, see
|
||||
[Reading Configuration](./reading.md) and
|
||||
[Defining Configuration](./defining.md).
|
||||
|
||||
## Further Reading
|
||||
|
||||
More details are provided in dedicated sections of the documentation.
|
||||
|
||||
- [Reading Configuration](./reading.md): How to read configuration in your
|
||||
plugin.
|
||||
- [Writing Configuration](./writing.md): How to provide configuration for your
|
||||
Backstage deployment.
|
||||
- [Defining Configuration](./defining.md): How to define configuration for users
|
||||
of your plugin.
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
id: reading
|
||||
title: Reading Backstage Configuration
|
||||
---
|
||||
|
||||
## Config API
|
||||
|
||||
There's a common configuration API for by both frontend and backend plugins. An
|
||||
API reference can be found [here](../reference/utility-apis/Config.md).
|
||||
|
||||
The configuration API is tailored towards failing fast in case of missing or bad
|
||||
config. That's because configuration errors can always be considered programming
|
||||
mistakes, and will fail deterministically.
|
||||
|
||||
### Type Safety
|
||||
|
||||
The methods for reading primitive values are typed, and validate that type at
|
||||
runtime. For example `getNumber()` requires the underlying value to be a number,
|
||||
and there will be no attempt to coerce other types into the desired one. If
|
||||
`getNumber()` receives a string value, it will throw an error, explaining where
|
||||
the bad config came from, and what the desired and actual types where.
|
||||
|
||||
### Reading Nested Configuration
|
||||
|
||||
The backing configuration data is a nested JSON structure, meaning there will be
|
||||
object, within objects, arrays within objects, and so on. There are a couple of
|
||||
different ways to access nested values when reading configuration, but the
|
||||
primary one is to use dot-separated paths.
|
||||
|
||||
For example, given the following configuration:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
baseUrl: http://localhost:3000
|
||||
```
|
||||
|
||||
We can access the `baseUrl` using `config.getString('app.baseUrl')`. Because of
|
||||
this syntax, configuration keys are not allowed to contain dots. In fact,
|
||||
configuration keys are validated using the following RegEx:
|
||||
`/^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i`.
|
||||
|
||||
Another option of accessing the `baseUrl` value is to create a sub-view of the
|
||||
configuration, `config.getConfig('app').getString('baseUrl')`. When reading out
|
||||
single values the dot-path pattern is preferred, but creating sub-views can be
|
||||
useful for when you want to pass on parts of configuration to be read out by a
|
||||
separate function. For example, given something like
|
||||
|
||||
```yaml
|
||||
my-plugin:
|
||||
items:
|
||||
a:
|
||||
title: Item A
|
||||
path: /a
|
||||
b:
|
||||
title: Item B
|
||||
path: /b
|
||||
```
|
||||
|
||||
You can get the list of all items using the `.keys()` method, and then pass on
|
||||
each sub-view to be handled individually.
|
||||
|
||||
```ts
|
||||
for (const itemKey of config.keys('my-plugin.items')) {
|
||||
const itemConfig = config.getConfig(`my-plugin.items`).getConfig(key);
|
||||
const item = createItemFromConfig(itemConfig);
|
||||
}
|
||||
```
|
||||
|
||||
Another option for iterating through configuration keys is to call
|
||||
`config.get('my-plugin.items')`, which simply returns the JSON structure for
|
||||
that position without any validation. This can be handy to use sometimes,
|
||||
especially if you're passing on config to an external library. There's a clear
|
||||
benefit to the sub-view approach though, which is that the user will receive
|
||||
much more detailed and relevant error messages. For example, if
|
||||
`itemConfig.getString('title')` fails in the above example because a boolean was
|
||||
supplied, the user will receive an error message with the full path, e.g.
|
||||
`my-plugin.items.b.title`, as well as the name of the config file with the bad
|
||||
value.
|
||||
|
||||
Note that no matter what method is used for reading out nested config, the same
|
||||
merging rules apply. You will always get the same value for any way of accessing
|
||||
nested config:
|
||||
|
||||
```ts
|
||||
// Equivalent as long as a.b.c exists and is a string
|
||||
config.getString('a.b.c');
|
||||
config.getConfig('a.b').getString('c');
|
||||
config.get('a').b.c;
|
||||
```
|
||||
|
||||
### Required vs Optional Configuration
|
||||
|
||||
Reading configuration can be divided into two categories: required, and
|
||||
optional. When reading optional configuration you use the optional methods such
|
||||
as `getOptionalString`. These methods will simply return `undefined` if
|
||||
configuration values are missing, allowing the called to fall back to default
|
||||
values. The optional methods still validate types however, so receiving a string
|
||||
in a call to `config.getOptionalNumber` will still throw an error.
|
||||
|
||||
A good pattern for reading optional configuration values is to use the `??`
|
||||
operator. For example:
|
||||
|
||||
```ts
|
||||
const title = config.getOptionalString('my-plugin.title') ?? 'My Plugin';
|
||||
```
|
||||
|
||||
To read required configuration, simply use the methods without `Optional`, for
|
||||
example `getString`. These will throw an error if there is no value available.
|
||||
|
||||
## Accessing ConfigApi in Frontend Plugins
|
||||
|
||||
The [ConfigApi](../reference/utility-apis/Config.md) in the frontend is a
|
||||
[UtilityApi](../api/utility-apis.md). It's accessible as usual via the
|
||||
`configApiRef` exported from `@backstage/core`.
|
||||
|
||||
Depending on the config api in another API is slightly different though, as the
|
||||
`ConfigApi` implementation is supplied via the App itself and not instantiated
|
||||
like other APIs. See
|
||||
[packages/app/src/apis.ts](https://github.com/spotify/backstage/blob/244eef851f5aa19f91c7c9b5c12d5df95cf482ca/packages/app/src/apis.ts#L66)
|
||||
for an example of how this wiring is done.
|
||||
|
||||
For standalone plugin setups in `dev/index.ts`, register a factory with a
|
||||
statically mocked implementation of the config API. Use the `ConfigReader` from
|
||||
`@backstage/config` to create an instance and register it for the `configApiRef`
|
||||
from `@backstage/core`.
|
||||
|
||||
## Accessing ConfigApi in Backend Plugins
|
||||
|
||||
In backend plugins the configuration is passed in via options from the main
|
||||
backend package. See for example
|
||||
[packages/backend/src/plugins/auth.ts](https://github.com/spotify/backstage/blob/244eef851f5aa19f91c7c9b5c12d5df95cf482ca/packages/backend/src/plugins/auth.ts#L23).
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
id: writing
|
||||
title: Writing Backstage Configuration Files
|
||||
---
|
||||
|
||||
## File Format
|
||||
|
||||
Configuration is stored in YAML format in `app-config.yaml` files, looking
|
||||
something like this:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
title: Backstage Example App
|
||||
baseUrl: http://localhost:3000
|
||||
|
||||
backend:
|
||||
listen: 0.0.0.0:7000
|
||||
baseUrl: http://localhost:7000
|
||||
|
||||
organization:
|
||||
name: Spotify
|
||||
|
||||
proxy:
|
||||
/my/api:
|
||||
target: https://example.com/api/
|
||||
changeOrigin: true
|
||||
pathRewrite:
|
||||
^/proxy/my/api/: /
|
||||
```
|
||||
|
||||
Configuration files are typically checked in and stored in the repo that houses
|
||||
the rest of the Backstage application.
|
||||
|
||||
## Environment Variable Overrides
|
||||
|
||||
Individual configuration values can be overridden using environment variables
|
||||
prefixed with `APP_CONFIG_`. Everything following that prefix in the environment
|
||||
variable name will be used as the config key, with `_` replaced by `.`. For
|
||||
example, to override the `app.baseUrl` value, set the `APP_CONFIG_app_baseUrl`
|
||||
environment variable to the desired value.
|
||||
|
||||
The value of the environment variable is parsed as JSON, but it will fall back
|
||||
to being interpreted as a string if it fails to parse. Note that if you for
|
||||
example want to pass on the string `"false"`, you need to wrap it in double
|
||||
quotes, e.g. `export APP_CONFIG_example='"false"'`.
|
||||
|
||||
While it may be tempting to use environment variable overrides for supplying a
|
||||
lot of configuration values, we recommend using them sparingly. Try to stick to
|
||||
using configuration files, and only use environment variables for things like
|
||||
reusing deployment artifacts across staging and production environments.
|
||||
|
||||
Note that environment variables work for frontend configuration too. They are
|
||||
picked up by the serve tasks of `@backstage/cli` for local development, and are
|
||||
injected by the entrypoint of the nginx container serving the frontend in a
|
||||
production build.
|
||||
|
||||
## File Resolution
|
||||
|
||||
It is possible to have multiple configuration files, both to support different
|
||||
environments, but also to define configuration that is local to specific
|
||||
packages.
|
||||
|
||||
All `app-config.yaml` files inside the monorepo root and package root are
|
||||
considered, as are files with additional `local` and environment affixes such as
|
||||
`development`, for example `app-config.local.yaml`,
|
||||
`app-config.production.yaml`, and `app-config.development.local.yaml`. Which
|
||||
environment config files are loaded is determined by the `NODE_ENV` environment
|
||||
variable. Local configuration files are always loaded, but are meant for local
|
||||
development overrides and should typically be `.gitignore`'d.
|
||||
|
||||
All loaded configuration files are merged together using the following rules:
|
||||
|
||||
- Configurations have different priority, higher priority means you replace
|
||||
values from configurations with lower priority.
|
||||
- Primitive values are completely replaced, as are arrays and all of their
|
||||
contents.
|
||||
- Objects are merged together deeply, meaning that if any of the included
|
||||
configs contain a value for a given path, it will be found.
|
||||
|
||||
The priority of the configurations is determined by the following rules, in
|
||||
order:
|
||||
|
||||
- Configuration from the `APP_CONFIG_` environment variables has the highest
|
||||
priority, followed by files.
|
||||
- Files inside package directories have higher priority than those in the root
|
||||
directory.
|
||||
- Files with environment affixes have higher priority than ones without.
|
||||
- Files with the `local` affix have higher priority than ones without.
|
||||
|
||||
## Secrets
|
||||
|
||||
Secrets are supported via a special `$secret` key, which in turn provides a
|
||||
number of different ways to read in secrets. To load a configuration value as a
|
||||
secret, supply an object with a single `$secret` key, and within that supply an
|
||||
object that describes how the secret is loaded. For example, the following will
|
||||
read the config key `backend.mySecretKey` from the environment variable
|
||||
`MY_SECRET_KEY`:
|
||||
|
||||
```yaml
|
||||
backend:
|
||||
mySecretKey:
|
||||
$secret:
|
||||
env: MY_SECRET_KEY
|
||||
```
|
||||
|
||||
With the above configuration, calling `config.getString('backend.mySecretKey')`
|
||||
will return the value of the environment variable `MY_SECRET_KEY` when the
|
||||
backend started up. All secrets are loaded at startup, so changing the contents
|
||||
of secret files or environment variables will not be reflected at runtime.
|
||||
|
||||
Note that secrets will never be included in the frontend bundle or development
|
||||
builds. When loading configuration you have to explicitly enable reading of
|
||||
secrets, which is only done for the backend configuration.
|
||||
|
||||
As hinted at, secrets can be loaded from a bunch of different sources, and can
|
||||
be extended with more. Below is a list of the currently supported methods for
|
||||
loading secrets.
|
||||
|
||||
### Env Secrets
|
||||
|
||||
This reads a secret from an environment variable. For example, the following
|
||||
config loads the secret from the `MY_SECRET` env var.
|
||||
|
||||
```yaml
|
||||
$secret:
|
||||
env: MY_SECRET
|
||||
```
|
||||
|
||||
### File Secrets
|
||||
|
||||
This reads a secret from the entire contents of a file. The file path is
|
||||
relative to the `app-config.yaml` the defines the secrets. For example, the
|
||||
following reads the contents of `my-secret.txt` relative to the config file
|
||||
itself:
|
||||
|
||||
```yaml
|
||||
$secret:
|
||||
file: ./my-secret.txt
|
||||
```
|
||||
|
||||
### Data File Secrets
|
||||
|
||||
This reads secrets from a path within a JSON-like data file. The file path
|
||||
behaves similar to file secrets, but in addition a `path` is used to point to a
|
||||
specific value inside the file. Supported file extensions are `.json`, `.yaml`,
|
||||
and `.yml`. For example, the following would read out `my-secret-key` from
|
||||
`my-secrets.json`:
|
||||
|
||||
```yaml
|
||||
$secret:
|
||||
data: ./my-secrets.json
|
||||
path: deployment.key
|
||||
|
||||
# my-secrets.json
|
||||
{
|
||||
"deployment": {
|
||||
"key": "my-secret-key"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -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`
|
||||
|
||||

|
||||

|
||||
|
||||
_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.
|
||||
|
||||

|
||||

|
||||
@@ -1,4 +1,9 @@
|
||||

|
||||
---
|
||||
id: design
|
||||
title: Design
|
||||
---
|
||||
|
||||

|
||||
|
||||
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
|
||||
we’ve crafted and add custom theming to better represent your organization!
|
||||
|
||||

|
||||

|
||||
|
||||
## ✅ 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 you’d like to help build up our design system, you can also add
|
||||
components we’ve 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
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: software-catalog-api
|
||||
title: API
|
||||
---
|
||||
|
||||
## TODO
|
||||
@@ -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)
|
||||
@@ -311,3 +336,195 @@ component, but there will always be one ultimate owner.
|
||||
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` | `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
|
||||
variables that the template requires using
|
||||
[JSON Forms Schema](https://jsonforms.io/).
|
||||
|
||||
Descriptor files for this kind may look as follows.
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Template
|
||||
metadata:
|
||||
name: react-ssr-template
|
||||
title: React SSR Template
|
||||
description:
|
||||
Next.js application skeleton for creating isomorphic web applications.
|
||||
tags:
|
||||
- recommended
|
||||
- react
|
||||
spec:
|
||||
owner: web@example.com
|
||||
templater: cookiecutter
|
||||
type: website
|
||||
path: '.'
|
||||
schema:
|
||||
required:
|
||||
- component_id
|
||||
- description
|
||||
properties:
|
||||
component_id:
|
||||
title: Name
|
||||
type: string
|
||||
description: Unique name of the component
|
||||
description:
|
||||
title: Description
|
||||
type: string
|
||||
description: Description of the component
|
||||
```
|
||||
|
||||
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 `Template`, respectively.
|
||||
|
||||
### `metadata.title` [required]
|
||||
|
||||
The nice display name for the template as a string, e.g. `React SSR Template`.
|
||||
This field is required as is used to reference the template to the user instead
|
||||
of the `metadata.name` field.
|
||||
|
||||
### `metadata.tags` [optional]
|
||||
|
||||
A list of strings that can be associated with the template, e.g.
|
||||
`['Recommended', 'React']`.
|
||||
|
||||
This list will also be used in the frontend to display to the user so you can
|
||||
potentially search and group templates by these tags.
|
||||
|
||||
### `spec.type` [optional]
|
||||
|
||||
The type of component as a string, e.g. `website`. This field is optional but
|
||||
recommended.
|
||||
|
||||
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, a website type component may present tooling in the Backstage interface
|
||||
that is specific to just websites.
|
||||
|
||||
The current set of well-known and common values for this field is:
|
||||
|
||||
- `service` - a backend service, typically exposing an API
|
||||
- `website` - a website
|
||||
- `library` - a software library, such as an NPM module or a Java library
|
||||
|
||||
### `spec.templater` [required]
|
||||
|
||||
The templating library that is supported by the template skeleton as a string,
|
||||
e.g `cookiecutter`.
|
||||
|
||||
Different skeletons will use different templating syntax, so it's common that
|
||||
the template will need to be run with a particular piece of software.
|
||||
|
||||
This key will be used to identify the correct templater which is registered into
|
||||
the `TemplatersBuilder`.
|
||||
|
||||
The values which are available by default are:
|
||||
|
||||
- `cookiecutter` - [cookiecutter](https://github.com/cookiecutter/cookiecutter).
|
||||
|
||||
### `spec.path` [optional]
|
||||
|
||||
The string location where the templater should be run if it is not on the same
|
||||
level as the `template.yaml` definition, e.g. `./cookiecutter/skeleton`.
|
||||
|
||||
This will set the `cwd` when running the templater to the folder path that you
|
||||
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
|
||||
|
||||

|
||||
|
||||
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.
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
id: software-catalog-overview
|
||||
title: Backstage Service Catalog (alpha)
|
||||
sidebar_label: Backstage Service Catalog
|
||||
---
|
||||
|
||||
## What is a Service Catalog?
|
||||
|
||||
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](descriptor-format.md) stored together with the code, which
|
||||
are then harvested and visualized in Backstage.
|
||||
|
||||

|
||||
|
||||
## How it works
|
||||
|
||||
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.
|
||||
|
||||
More specifically, the Service Catalog enables two main use-cases:
|
||||
|
||||
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`.
|
||||
|
||||

|
||||
|
||||
## 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:
|
||||
|
||||

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

|
||||
|
||||
It is important to note that any kind of software can be registered in
|
||||
Backstage. Even if the software is not maintained by your company (SaaS
|
||||
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.
|
||||
|
||||
### Updating component metadata
|
||||
|
||||
Teams owning the components are responsible for maintaining the metadata about
|
||||
them, and do so using their normal Git workflow.
|
||||
|
||||

|
||||
|
||||
Once the change has been merged, Backstage will automatically show the updated
|
||||
metadata in the service catalog after a short while.
|
||||
|
||||
## 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.
|
||||
|
||||

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

|
||||
|
||||
## Integrated tooling through plugins
|
||||
|
||||
The service catalog is a great way to 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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
### 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)
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
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
|
||||
good to also have some files in there that can be templated in.
|
||||
|
||||
A simple `template.yaml` definition might look something like this:
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Template
|
||||
metadata:
|
||||
# unique name per namespace for the template
|
||||
name: react-ssr-template
|
||||
# title of the template
|
||||
title: React SSR Template
|
||||
# a description of the template
|
||||
description:
|
||||
Next.js application skeleton for creating isomorphic web applications.
|
||||
# some tags to display in the frontend
|
||||
tags:
|
||||
- recommended
|
||||
- react
|
||||
spec:
|
||||
# which templater key to use in the templaters builder
|
||||
templater: cookiecutter
|
||||
# what does this template create
|
||||
type: website
|
||||
# if the template is not in the current directory where this definition is kept then specfiy
|
||||
path: './template'
|
||||
# the schema for the form which is displayed in the frontend.
|
||||
# should follow JSON schema for forms: https://jsonforms.io/
|
||||
schema:
|
||||
required:
|
||||
- component_id
|
||||
- description
|
||||
properties:
|
||||
component_id:
|
||||
title: Name
|
||||
type: string
|
||||
description: Unique name of the component
|
||||
description:
|
||||
title: Description
|
||||
type: string
|
||||
description: Description of the component
|
||||
```
|
||||
|
||||
[Template Entity](../software-catalog/descriptor-format.md#kind-template)
|
||||
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
|
||||
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
|
||||
running:
|
||||
|
||||
```sh
|
||||
curl \
|
||||
--location \
|
||||
--request POST 'localhost:7000/catalog/locations' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw "{\"type\": \"file\", \"target\": \"${YOUR PATH HERE}/template.yaml\"}"
|
||||
```
|
||||
|
||||
If loading from a git location, you can run the following
|
||||
|
||||
```sh
|
||||
curl \
|
||||
--location \
|
||||
--request POST 'localhost:7000/catalog/locations' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw "{\"type\": \"github\", \"target\": \"https://${YOUR GITHUB REPO}blob/master/${PATH TO FOLDER}/template.yaml\"}"
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
yarn lerna run mock-data
|
||||
```
|
||||
|
||||
The `type` field which is chosen in the request to add the `template.yaml` to
|
||||
the Service Catalog here, will be come the `PreparerKey` which will be used to
|
||||
select the `Preparer` when creating a job.
|
||||
|
||||
### Adding form values in the Scaffolder Wizard
|
||||
|
||||
The `spec.schema` property in the
|
||||
[Template Entity](../software-catalog/descriptor-format.md#kind-template) is a
|
||||
`yaml` version of the JSON Form Schema standard.
|
||||
|
||||
Here you can define the key/values and then the wizard will convert this to a
|
||||
form for the user to fill in when your template is selected.
|
||||
|
||||
You can find out much more about the standard and how to use it here:
|
||||
https://jsonforms.io
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
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
|
||||
making a temporary folder with the contents of the selected skeleton.
|
||||
|
||||
Currently, we provide two different providers that can parse two different
|
||||
location protocols:
|
||||
|
||||
- `file://`
|
||||
- `github://`
|
||||
|
||||
These two are added to the `PreparersBuilder` and then passed into the
|
||||
`createRouter` function of the `@spotify/plugin-scaffolder-backend`
|
||||
|
||||
An full example backend can be found
|
||||
[here](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts),
|
||||
but it looks something like the following
|
||||
|
||||
```ts
|
||||
import {
|
||||
createRouter,
|
||||
FilePreparer,
|
||||
GithubPreparer,
|
||||
Preparers,
|
||||
} from '@backstage/plugin-scaffolder-backend';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({ logger }: PluginEnvironment) {
|
||||
const preparers = new Preparers();
|
||||
|
||||
const filePreparer = new FilePreparer();
|
||||
const githubPreparer = new GithubPreparer();
|
||||
|
||||
preparers.register('file', filePreparer);
|
||||
preparers.register('github', githubPreparer);
|
||||
|
||||
return await createRouter({
|
||||
preparers,
|
||||
templaters,
|
||||
logger,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
As you can see in the above code, a `PreparerBuilder` is created, and then two
|
||||
of the `preparers` are registered with the different protocols that they accept.
|
||||
|
||||
The `protocol` is set on the
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template)
|
||||
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
|
||||
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.
|
||||
|
||||
### Creating your own Preparer to add to the `PreparerBuilder`
|
||||
|
||||
All preparers need to implement the `PreparerBase` type.
|
||||
|
||||
That type looks like the following:
|
||||
|
||||
```ts
|
||||
export type PreparerBase = {
|
||||
prepare(
|
||||
template: TemplateEntityV1alpha1,
|
||||
opts: { logger: Logger },
|
||||
): Promise<string>;
|
||||
};
|
||||
```
|
||||
|
||||
The `prepare` function will be given the
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template)
|
||||
along with the source of where the `template.yaml` was loaded from under the
|
||||
`metedata.annotations.managed-by-location` property.
|
||||
|
||||
Now it's up to you to implement a function which can go and fetch the skeleton
|
||||
and put the contents into a temporary directory and return that directory path.
|
||||
|
||||
Some good examples exist here:
|
||||
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts
|
||||
|
||||
### Registerinng your own Preparer
|
||||
|
||||
You can register the preparer that you have created with the `PreparerBuilder`
|
||||
by using the `PreparerKey` from the Catalog, for example like this:
|
||||
|
||||
```ts
|
||||
const preparers = new Preparers();
|
||||
preparers.register('gcs', new GoogleCloudStoragePreparer());
|
||||
```
|
||||
|
||||
And then pass this in to the `createRouter` function.
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
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
|
||||
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.
|
||||
|
||||
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,
|
||||
but PR's are always welcome.
|
||||
|
||||
An full example backend can be found
|
||||
[here](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts),
|
||||
but it looks something like the following
|
||||
|
||||
```ts
|
||||
import {
|
||||
createRouter,
|
||||
GithubPublisher,
|
||||
} from '@backstage/plugin-scaffolder-backend';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({ logger }: PluginEnvironment) {
|
||||
const githubClient = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN });
|
||||
const publisher = new GithubPublisher({ client: githubClient });
|
||||
|
||||
return await createRouter({
|
||||
publisher,
|
||||
logger,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The publisher will always be called with the location from the selected
|
||||
`Preparer`.
|
||||
|
||||
### Create your own Publisher and register it with the Scaffolder
|
||||
|
||||
All `publishers` need to implement the `PublisherBase` type.
|
||||
|
||||
That type looks like the following:
|
||||
|
||||
```ts
|
||||
export type PublisherBase = {
|
||||
publish(opts: {
|
||||
entity: TemplateEntityV1alpha1;
|
||||
values: RequiredTemplateValues & Record<string, JsonValue>;
|
||||
directory: string;
|
||||
}): Promise<{ remoteUrl: string }>;
|
||||
};
|
||||
```
|
||||
|
||||
The `publisher` function will be called with an `options` object which contains
|
||||
the following:
|
||||
|
||||
- `entity` - the
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template)
|
||||
which is currently being scaffolded
|
||||
- `values` - a json object which will resemble the `spec.schema` from the
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template)
|
||||
which is defined here under spec.schema`. More info can be found here
|
||||
[Register your own template](../adding-templates.md#adding-form-values-in-the-scaffolder-wizard)
|
||||
- `directory` - a string containing the returned path from the `templater`. See
|
||||
more information here in
|
||||
[Create your own templater](./create-your-own-templater.md)
|
||||
|
||||
Now it's up to you to implement the `publish` function and return
|
||||
`{ remoteUrl: string }` which can be used to identify the finished product.
|
||||
|
||||
Some good examples exist here:
|
||||
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts
|
||||
|
||||
### Registering your own Publisher
|
||||
|
||||
Currently, we only support one `publisher` (PR's welcome), but you can register
|
||||
any single `publisher` with the `createRouter`.
|
||||
|
||||
```ts
|
||||
return await createRouter({
|
||||
publisher: new MyGitlabPublisher(),
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
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
|
||||
interpolate into the skeleton files.
|
||||
|
||||
Currently we provide the following templaters:
|
||||
|
||||
- `cookiecutter`
|
||||
|
||||
This templater is added the `TemplaterBuilder` and then passed into the
|
||||
`createRouter` function of the `@spotify/plugin-scaffolder-backend`
|
||||
|
||||
An full example backend can be found
|
||||
[here](https://github.com/spotify/backstage/blob/d91c10f654475a60829fa33a5c81018e517a319a/packages/backend/src/plugins/scaffolder.ts),
|
||||
but it looks something like the following
|
||||
|
||||
```ts
|
||||
import {
|
||||
CookieCutter,
|
||||
createRouter,
|
||||
Templaters,
|
||||
} from '@backstage/plugin-scaffolder-backend';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({ logger }: PluginEnvironment) {
|
||||
const templaters = new Templaters();
|
||||
const cookiecutterTemplater = new CookieCutter();
|
||||
templaters.register('cookiecutter', cookiecutterTemplater);
|
||||
|
||||
return await createRouter({
|
||||
templaters,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
As you can see in the above code a `TemplaterBuilder` is created and the default
|
||||
`cookiecutter` `templater` is registered under the key `cookicutter`.
|
||||
|
||||
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
|
||||
`TemplaterBuilder`.
|
||||
|
||||
### Creating your own Templater to add to the `TemplaterBuilder`
|
||||
|
||||
All templaters need to implement the `TemplaterBase` type.
|
||||
|
||||
That type looks like the following:
|
||||
|
||||
```ts
|
||||
export type TemplaterRunOptions = {
|
||||
directory: string;
|
||||
values: RequiredTemplateValues & Record<string, JsonValue>;
|
||||
logStream?: Writable;
|
||||
dockerClient: Docker;
|
||||
};
|
||||
|
||||
export type TemplaterBase = {
|
||||
run(opts: TemplaterRunOptions): Promise<TemplaterRunResult>;
|
||||
};
|
||||
```
|
||||
|
||||
The `run` function will be given a `TemplaterRunOptions` object which is as
|
||||
follows:
|
||||
|
||||
- `directory`- the skeleton directory returned from the `Preparer`, more info at
|
||||
[Create your own preparer](./create-your-own-preparer.md).
|
||||
- `values` - a json object which will resemble the `spec.schema` from the
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template)
|
||||
which is defined here under spec.schema`. More info can be found here
|
||||
[Register your own template](../adding-templates.md#adding-form-values-in-the-scaffolder-wizard)
|
||||
- `logStream` - a stream that you can write to for displaying in the frontend.
|
||||
- `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
|
||||
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
|
||||
downloads the folder and does everything using an AMI if you want. It's entirely
|
||||
up to you!
|
||||
|
||||
Now it's up to you to implement the `run` function, and then return a
|
||||
`TemplaterRunResult` which is `{ resultDir: string }`.
|
||||
|
||||
Some good examples exist here:
|
||||
|
||||
- https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts
|
||||
|
||||
### Registering your own Templater
|
||||
|
||||
If you try to process a
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template)
|
||||
with a new `spec.templater` value, you'll need to register that with the
|
||||
`TemplaterBuilder`.
|
||||
|
||||
For example let's say you have the following
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template):
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Template
|
||||
metadata:
|
||||
name: react-ssr-template
|
||||
title: React SSR Template
|
||||
description:
|
||||
Next.js application skeleton for creating isomorphic web applications.
|
||||
tags:
|
||||
- recommended
|
||||
- react
|
||||
spec:
|
||||
owner: web@example.com
|
||||
templater: handlebars
|
||||
type: website
|
||||
path: '.'
|
||||
schema:
|
||||
required:
|
||||
- component_id
|
||||
- description
|
||||
properties:
|
||||
component_id:
|
||||
title: Name
|
||||
type: string
|
||||
description: Unique name of the component
|
||||
description:
|
||||
title: Description
|
||||
type: string
|
||||
description: Description of the component
|
||||
```
|
||||
|
||||
You see that the `spec.templater` is set as `handlebars`, you'll need to
|
||||
register this with the `TemplaterBuilder` like so:
|
||||
|
||||
```ts
|
||||
const templaters = new Templaters();
|
||||
templaters.register('handlebars', new HandlebarsTemplater());
|
||||
```
|
||||
|
||||
And then pass this in to the `createRouter` function.
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
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?
|
||||
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
|
||||
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.
|
||||
|
||||
1. Pick a skeleton
|
||||
2. Template some variables into the skeleton
|
||||
3. Send the templated skeleton somewhere
|
||||
|
||||
These three steps are translated to the folllowing stages under the hood in the
|
||||
scaffolder that you will need to know:
|
||||
|
||||
1. Prepare
|
||||
2. Template
|
||||
3. Publish
|
||||
|
||||
Each of these steps can be configured for your own use case, but we provide some
|
||||
sensible defaults too.
|
||||
|
||||
Lets dive a little deeper into these phases.
|
||||
|
||||
### Glossary and Jargon
|
||||
|
||||
**Preparer** - The preparer is responsible for fetching the skeleton code and
|
||||
placing it into a directory and then will return that directory. It is
|
||||
registered with the `Preparers` with a particular type, which is then used in
|
||||
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
|
||||
the host machine.
|
||||
|
||||
**Publisher** - The publisher is responsible for taking the finished directory,
|
||||
and publishing it to a remote registry. This could be a Git repository or
|
||||
something similar. Right now, the scaffolder only supports one publishing method
|
||||
for the entire lifecycle, but it could be configured from the frontend and
|
||||
passed through to the scaffolder backend.
|
||||
|
||||
### How it works
|
||||
|
||||
The main 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
|
||||
`GET /v1/job/:jobId`
|
||||
|
||||
To create a scaffolding job, a JSON object containing the
|
||||
[Template Entity](../../software-catalog/descriptor-format.md#kind-template) +
|
||||
additional templating values must be posted as the post body.
|
||||
|
||||
```js
|
||||
{
|
||||
"template": {
|
||||
"apiVersion": "backstage/v1alpha1",
|
||||
"kind": "Template",
|
||||
// more stuff here
|
||||
},
|
||||
"values": {
|
||||
"component_id": "test",
|
||||
"description": "somethingelse"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
job processor will complete each stage before moving onto the next stage, whilst
|
||||
collecting logs and mutating the running job.
|
||||
|
||||
Here's some futher reading that you might find useful:
|
||||
|
||||
- [Adding your own Template](../adding-templates.md)
|
||||
- [Creating your own Templater](./create-your-own-templater.md)
|
||||
- [Creating your own Publisher](./create-your-own-publisher.md)
|
||||
- [Creating your own Preparer](./create-your-own-preparer.md)
|
||||