Merge branch 'ebarriosjr/Creating-gcp-projects-plugin' of github.com:ebarriosjr/backstage into ebarriosjr/Creating-gcp-projects-plugin

This commit is contained in:
ebarrios
2020-07-28 17:14:30 +02:00
351 changed files with 11601 additions and 2200 deletions
+8 -6
View File
@@ -4,9 +4,11 @@
# The last matching pattern takes precedence.
# https://help.github.com/articles/about-codeowners/
* @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
* @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
+17
View File
@@ -13,6 +13,18 @@ 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]
@@ -44,6 +56,11 @@ jobs:
- run: yarn tsc
- run: yarn build
- name: verify app and plugin creation
env:
POSTGRES_HOST: localhost
POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }}
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
run: |
sudo sysctl fs.inotify.max_user_watches=524288
node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js
-3
View File
@@ -76,8 +76,5 @@ 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
+37
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -122,3 +122,6 @@ dist
# Temporary change files created by Vim
*.swp
# MkDocs build output
site
+13 -3
View File
@@ -1,3 +1,13 @@
| 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 |
+4
View File
@@ -54,6 +54,10 @@ 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) 👏🏻💯
+8 -10
View File
@@ -59,11 +59,9 @@ 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.
- [**proxy**](https://github.com/spotify/backstage/tree/master/plugins/proxy-backend) - Terminates HTTPS and exposes any RESTful API to Plugins.
- **identity** - A backend service that holds your organisation's metadata.
_\* not yet released_
## Getting started
To run a Backstage app, you will need to have the following installed:
@@ -86,17 +84,16 @@ And that's it! You are good to go 👍
### Next step
Take a look at the [Getting Started](docs/getting-started/README.md) guide to learn more about how to extend the functionality with Plugins.
Take a look at the [Getting Started](docs/getting-started/index.md) guide to learn how to set up Backstage, and how to develop on the platform.
## 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)
- [Main documentation](docs/README.md)
- [Service Catalog](docs/features/software-catalog/index.md)
- [Create a Backstage App](docs/getting-started/create-an-app.md)
- [Architecture](docs/overview/architecture-terminology.md) ([Decisions](docs/architecture-decisions/index.md))
- [Designing for Backstage](docs/dls/design.md)
- [Storybook - UI components](http://storybook.backstage.io)
- [Contributing to Storybook](docs/getting-started/contributing-to-storybook.md)
## Contributing
@@ -109,6 +106,7 @@ We would love your help in building Backstage! See [CONTRIBUTING](CONTRIBUTING.m
- [RFCs](https://github.com/spotify/backstage/labels/rfc) - Help shape the technical direction
- [FAQ](docs/FAQ.md) - 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 ❤️
+73 -1
View File
@@ -15,10 +15,82 @@ proxy:
target: 'https://circleci.com/api/v1.1'
changeOrigin: true
pathRewrite:
'^/circleci/api/': '/'
'^/proxy/circleci/api/': '/'
organization:
name: Spotify
techdocs:
storageUrl: https://techdocs-mock-sites.storage.googleapis.com
sentry:
organization: spotify
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
gitlab:
development:
appOrigin: "http://localhost:3000/"
secure: false
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:
appOrigin: "http://localhost:3000/"
secure: false
clientId:
$secret:
env: AUTH_OKTA_CLIENT_ID
clientSecret:
$secret:
env: AUTH_OKTA_CLIENT_SECRET
audience:
$secret:
env: AUTH_OKTA_AUDIENCE
oauth2:
development:
appOrigin: "http://localhost:3000/"
secure: false
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
+16
View File
@@ -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
+4 -1
View File
@@ -22,7 +22,10 @@ function inject_config() {
>&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
+98 -8
View File
@@ -1,10 +1,100 @@
# Documentation
# Documentation structure
Check out <https://backstage.io> or see the table of contents below.
**Note!** This documentation structure is very much work in progress. If (when,
really 😆) you find broken links or missing content, please create an issue or,
better yet, a pull request.
- [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)
- Overview
- [What is Backstage?](overview/what-is-backstage.md)
- [Backstage architecture](overview/architecture-overview.md)
- [Architecture and terminology](overview/architecture-terminology.md)
- [Roadmap](overview/roadmap.md)
- Getting started
- [Running Backstage locally](getting-started/index.md)
- [Installation](getting-started/installation.md)
- [Local development](getting-started/development-environment.md)
- [Demo deployment](https://backstage-demo.roadie.io)
- Production deployments
- [Create an App](getting-started/create-an-app.md)
- App configuration
- [Configuring App with plugins](getting-started/configure-app-with-plugins.md)
- [Customize the look-and-feel of your App](getting-started/app-custom-theme.md)
- Deployment scenarios
- [Kubernetes](getting-started/deployment-k8s.md)
- [Other](getting-started/deployment-other.md)
- Features
- Software Catalog
- [Overview](features/software-catalog/index.md)
- [System model](features/software-catalog/system-model.md)
- [YAML File Format](features/software-catalog/descriptor-format.md)
- [Populating the catalog](features/software-catalog/populating.md)
- [Extending the model](features/software-catalog/extending-the-model.md)
- [External integrations](features/software-catalog/external-integrations.md)
- [API](features/software-catalog/api.md)
- Software creation templates
- [Overview](features/software-templates/index.md)
- [Adding templates](features/software-templates/adding-templates.md)
- Extending the Scaffolder:
- [Overview](features/software-templates/extending/index.md)
- [Create your own Templater](features/software-templates/extending/create-your-own-templater.md)
- [Create your own Publisher](features/software-templates/extending/create-your-own-publisher.md)
- [Create your own Preparer](features/software-templates/extending/create-your-own-preparer.md)
- Docs-like-code
- [Overview](features/techdocs/README.md)
- [Getting Started](features/techdocs/getting-started.md)
- [Concepts](features/techdocs/concepts.md)
- [Reading Documentation](features/techdocs/reading-documentation.md)
- [Writing Documentation](features/techdocs/writing-documentation.md)
- [Publishing Documentation](features/techdocs/publishing-documentation.md)
- [Contributing](features/techdocs/contributing.md)
- [Debugging](features/techdocs/debugging.md)
- [FAQ](features/techdocs/FAQ.md)
- Plugins
- [Overview](plugins/index.md)
- [Existing plugins](plugins/existing-plugins.md)
- [Creating a new plugin](plugins/create-a-plugin.md)
- [Developing a plugin](plugins/plugin-development.md)
- [Structure of a plugin](plugins/structure-of-a-plugin.md)
- Backends and APIs
- [Proxying](plugins/proxying.md)
- [Backstage backend plugin](plugins/backend-plugin.md)
- [Call existing API](plugins/call-existing-api.md)
- Testing
- [Overview](plugins/testing.md)
- Publishing
- [Open source and NPM](plugins/publishing.md)
- [Private/internal (non-open source)](plugins/publish-private.md)
- Authentication and identity
- [Overview](auth/index.md)
- [Add auth provider](auth/add-auth-provider.md)
- [Auth backend](auth/auth-backend.md)
- [OAuth](auth/oauth.md)
- [Glossary](auth/glossary.md)
- Designing for Backstage
- [Backstage Design Language System (DLS)](dls/design.md)
- [Storybook -- reusable UI components](http://storybook.backstage.io)
- [Contributing to Storybook](dls/contributing-to-storybook.md)
- [Figma resources](dls/figma.md)
- API references
- TypeScript API
- [Utility APIs](api/utility-apis.md)
- [Utility API References](reference/utility-apis/README.md)
- [createPlugin](reference/createPlugin.md)
- [createPlugin-feature-flags](reference/createPlugin-feature-flags.md)
- [createPlugin-router](reference/createPlugin-router.md)
- Backend APIs
- [Backend](api/backend.md)
- Tutorials
- [Overview](tutorials/index.md)
- Architecture Decision Records (ADRs)
- [Overview](architecture-decisions/index.md)
- [ADR001 - Architecture Decision Record (ADR) log](architecture-decisions/adr001-add-adr-log.md)
- [ADR002 - Default Software Catalog File Format](architecture-decisions/adr002-default-catalog-file-format.md)
- [ADR003 - Avoid Default Exports and Prefer Named Exports](architecture-decisions/adr003-avoid-default-exports.md)
- [ADR004 - Module Export Structure](architecture-decisions/adr004-module-export-structure.md)
- [ADR005 - Catalog Core Entities](architecture-decisions/adr005-catalog-core-entities.md)
- [ADR006 - Avoid React.FC and React.SFC](architecture-decisions/adr006-avoid-react-fc.md)
- [ADR007 - Use MSW for Mocking Network Requests](architecture-decisions/adr007-use-msw-to-mock-service-requests.md)
- [Contribute](../CONTRIBUTING.md)
- [Support](overview/support.md)
- [FAQ](FAQ.md)
View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

@@ -19,7 +19,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`.
@@ -1,6 +1,8 @@
## Date: 2020-04-26
# ADR001: Architecture Decision Record (ADR) log
## 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
@@ -0,0 +1,60 @@
# ADR007: Use MSW to mock http requests
## 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`
+28
View File
@@ -0,0 +1,28 @@
# Architecture Decision Records (ADR)
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 full path of the ADR to the [`mkdocs.yml`](/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.
View File
+3 -2
View File
@@ -25,8 +25,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
+9 -7
View File
@@ -8,11 +8,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 +52,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
View File

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

+1
View File
@@ -0,0 +1 @@
We have a [Figma component library](https://www.figma.com/@backstage) that you can use to build your own plugins for Backstage.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

@@ -311,3 +311,114 @@ 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.
## Kind: Template
Describes the following entity kind:
| Field | Value |
| -------------------- | ----------------------- |
| `apiVersion` | `backstage.io/v1alpha1` |
| `Kind: Templatekind` | `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.
+26
View File
@@ -0,0 +1,26 @@
# Backstage Service Catalog (alpha)
## 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](../../architecture-decisions/adr002-default-catalog-file-format.md#format)
stored together with the code, which are then harvested and visualized in
Backstage.
![service-catalog](https://backstage.io/blog/assets/6/header.png)
We have also found that the service catalog is a great way to organise the
infrastructure tools you use to manage the software as well. This is how
Backstage creates one developer portal for all your tools. Rather than asking
teams to jump between different infrastructure UIs (and incurring additional
cognitive overhead each time they make a context switch), most of these tools
can be organised around the entities in the catalog.
## Using the Service Catalog
TODO
![](service-catalog-home.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

@@ -0,0 +1,102 @@
# 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
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

@@ -0,0 +1,97 @@
# 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,92 @@
# 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,146 @@
# 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,87 @@
## 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)
+59
View File
@@ -0,0 +1,59 @@
# Software Templates
The Software Templates part of Backstage is a tool that can help you create
Components inside Backstage. It by default has the ability to load skeletons of
code, template in some variables and then publish the template to some location
like GitHub.
### Getting Started
The Software Templates are available under `/create`, and if you've followed
[Getting Started with Backstage](../../getting-started), you should be able to
reach `http://localhost:3000/create`.
You should get something that looks similar to this:
![Create Image](./assets/create.png)
### Choose a template
When you select a template that you want to create, you'll be taken to the next
page which may or may not look different for each template. Each template can
ask for different input variables, and they are then passed to the templater
internally.
![Enter some variables](./assets/template-picked.png)
After filling in these variables, you'll get some more fields to fill out which
are required for backstage usage. The owner, which is a `user` in the backstage
system, and the `storePath` which right now must be a Github Organisation and a
non-existing github repository name in the format `organistaion/reponame`.
![Enter backstage vars](./assets/template-picked-2.png)
### Run!
Once you've entered values and confirmed, you'll then get a modal with live
progress of what is currently happening with the creation of your template.
![Templating Running](./assets/running.png)
It shouldn't take too long, and you'll have a success screen!
![Templating Complete](./assets/complete.png)
If it fails, you'll be able to click on each section to get the log from the
step that failed which can be helpful to debug.
![Templating failed](./assets/failed.png)
### View Component in Catalog
When it's been created you'll see the `View in Catalog` button, which will take
you to the registered component in the catalog:
![Catalog](./assets/go-to-catalog.png)
And then you'll also be able to see it in the Catalog View table
![Catalog](./assets/added-to-the-catalog-list.png)
+29 -1
View File
@@ -1 +1,29 @@
# FAQ
# TechDocs FAQ
This page answer frequently asked questions about [TechDocs].
#### Technology
- [What static site generator is TechDocs using?](./#what-static-site-generator-is-techdocs-using)
- [What is the mkdocs-techdocs-core plugin?](./#what-is-the-mkdocs-techdocs-core-plugin)
## What static site generator is TechDocs using?
TechDocs is using [MkDocs](https://www.mkdocs.org/) to build project
doucmentation under the hood. Documentation built with the
[techdocs-container](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/README.md)
is using the MkDocs Material Theme.
## What is the mkdocs-techdocs-core plugin?
The
[mkdocs-techdocs-core](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md)
package is a MkDocs Plugin that works like a wrapper around multiple MkDocs
plugins (e.g.
[MkDocs Monorepo Plugin](https://github.com/spotify/mkdocs-monorepo-plugin)) as
well as a selection of Python Markdown extensions that TechDocs supports.
_Add a question that you think others might be interested in? Edit the file
[here](https://github.com/spotify/backstage/edit/master/docs/features/techdocs/FAQ.md)._
[techdocs]: README.md
+31 -11
View File
@@ -2,17 +2,41 @@
## What is it?
Intro, backstory, etc.
<!-- Intro, backstory, etc.: -->
Wait, what is TechDocs? TechDocs is Spotifys homegrown docs-like-code solution
built directly into Backstage. Today, it is now one of the core products in
Spotifys developer experience offering with 2,400+ documentation sites and
1,000+ engineers using it daily.
## Features
- A centralized place to discover documentation.
- A clear end-to-end docs-like-code solution. (_Coming soon in V.1_)
- A tightly coupled feedback loop with the developer workflow. (_Coming soon in
V.2_)
- A developer ecosystem for creating extensions. (_Coming soon in V.2_)
## Project roadmap
| Version | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| [TechDocs V.0 ✅][v0] | Read docs in Backstage - Enable anyone to get a reader experience working in Backstage. |
| [TechDocs V.1 🚧][v1] | TechDocs end to end - First and minimum release of TechDocs that you can use end to end - and contribute to. |
| [TechDocs V.2 🔮⌛][v2] | Widget Architecture - TechDocs widget architecture available, so the community can create their own customized features. |
[v0]: https://github.com/spotify/backstage/milestone/15
[v1]: https://github.com/spotify/backstage/milestone/16
[v2]: https://github.com/spotify/backstage/milestone/17
## Structure
- [Getting Started]
- [Concepts]
- [Reading Documentation]
- [Writing Documentation]
- [Publishing Documentation]
- [Contributing]
- [Debugging]
- [Creating and Publishing Documentation]
- [FAQ]
## Tech Stack
@@ -26,11 +50,7 @@ Intro, backstory, etc.
[getting started]: getting-started.md
[concepts]: concepts.md
[reading documentation]: reading-documentation.md
[writing documentation]: writing-documentation.md
[publishing documentation]: publishing-documentation.md
[contributing]: contributing.md
[debugging]: debugging.md
[creating and publishing documentation]: creating-and-publishing.md
[faq]: FAQ.md 'Frequently asked questions'
[techdocs/frontend]:
https://github.com/spotify/backstage/blob/master/plugins/techdocs
+52
View File
@@ -1 +1,53 @@
# Concepts
This page describes concepts that has been introduced with Spotify's
docs-like-code solution in Backstage.
### TechDocs Core Plugin
The TechDocs Core Plugin is a MkDocs plugin created as a wrapper around multiple
MkDocs plugins and Python Markdown extensions to standardize the configuration
of MkDocs used for TechDocs.
[TechDocs Core](../../../packages/techdocs-container/techdocs-core/README.md)
### TechDocs container
The TechDocs container is a Docker container available at
[DockerHub](https://hub.docker.com/r/spotify/techdocs). It builds static HTML
pages, including stylesheets and scripts from Python flavored Markdown, through
MkDocs.
[TechDocs Container](../../../packages/techdocs-container/README.md)
### TechDocs publisher (Coming Soon)
### TechDocs CLI
The TechDocs CLI was created to make it easy to write, generate and preview
documentation for publishing. Currently it mostly acts as a wrapper around the
TechDocs container and provides a easy to use interface for our docker
container.
[TechDocs CLI](../../../packages/techdocs-cli/README.md)
### TechDocs Reader
Documentation generated by TechDocs is generated as static html sites. The
TechDocs Reader was therefore created to be able to integrate pre-generated html
sites with the Backstage UI.
The TechDocs Reader purpose is also to open up the opportunity to integrate
TechDocs widgets for a customized full-featured TechDocs experience.
([Coming Soon V.2](https://github.com/spotify/backstage/milestone/17))
[TechDocs Reader](../../../plugins/techdocs/src/reader/README.md)
### Transformers
Transformers is different pieces of functionality used inside the TechDocs
Reader. The reason to why transformers were introduced is to provide a way to
transform the html content on pre and post render. (e.g. rewrite docs links or
modify css)
[Transformers API docs](../../../plugins/techdocs/src/reader/transformers/README.md)
-1
View File
@@ -1 +0,0 @@
# Contributing
@@ -0,0 +1,134 @@
# Creating and publishing your docs
This section will guide you through:
- Creating a basic setup for your documentation
- Writing and previewing your documentation in a local Backstage environment
- Creating a build ready for publication
- Publishing your documentation and making your Backstage instance read from
your published docs.
## Prerequisities
- [Docker](https://docs.docker.com/get-docker/)
- Static file hosting
- A working Backstage instance with TechDocs installed
[TechDocs getting started](getting-started.md)
## Create a basic documentation setup
Create a directory that contains your documentation. Inside this directory you
should create a file called `mkdocs.yml`. As an example you can create a
directory called `hello-docs` in your home directory (also known as `~`). Below
is a basic example of how it could look.
The `~/hello-docs/mkdocs.yml` file should have the following content:
```yaml
site_name: 'example-docs'
nav:
- Home: index.md
plugins:
- techdocs-core
```
And then the `~/hello-docs/docs/index.md` should have the following content:
```md
# example docs
This is a basic example of documentation.
```
## Writing and previewing your documentation
Using the `techdocs-cli` you can preview your docs inside a local Backstage
instance and get automatic recompilation on changes. This is useful for when you
want to write your documentation.
To do this you can run:
```bash
cd ~/hello-docs/
npx techdocs-cli serve
```
## Build production ready documentation
To get a build suitable for publication you can build your docs using the
`spotify/techdocs` container.
```bash
cd ~/hello-docs/
docker run -it -w /content -v $(pwd):/content spotify/techdocs build
```
You should now have a folder called `~/hello-docs/site/`.
## Deploy to a file server
In order to serve documentation to TechDocs, our Backstage plugin needs to
download the HTML rendered from the
[Create documentation](#create-documentation) step above. This will likely exist
on an external file server, or a storage solution such as Google Cloud Storage.
When deploying documentation, it should be deployed on that file server /
storage solution with the following convention: `{id}/{file}`. For example, if
we wanted to upload the `getting-started/index.html` file for the `backstage`
documentation site, we would upload it to our file server as
`backstage/getting-started/index.html`.
To explain further how this would look like for multiple documentation sites,
take a look at this example file tree that would be represented on your file
server:
```md
/backstage/index.html /backstage/getting-started/index.html
/backstage/contributing/index.html /mkdocs/index.html
/mkdocs/plugin-development/index.html
/mkdocs/plugin-development/debugging/index.html
```
In this file tree, we have two documentation sites available: `backstage` and
`mkdocs`. Each of them expose several pages. Let's say both of these are hosted
on `http://example.com` as the server URL.
When you configure the TechDocs plugin in Backstage to use `http://example.com`
as the file server / storage solution, it will translate the following URLs to
the file server:
| Backstage URL | File Server URL |
| --------------------------------------------------------- | ------------------------------------------------------- |
| https://demo.backstage.io/docs/backstage/ | http://example.com/backstage/index.html |
| https://demo.backstage.io/docs/mkdocs/plugin-development/ | http://example.com/mkdocs/plugin-development/index.html |
Then deploying new sites is easy. It's as simple as copying over the `site/`
folder produced in the [Create documentation](#create-documentation) step above
and copying it over to the file server / storage solution under the ID of the
documentation site. It will then become immediately available in Backstage under
the same ID as you can see in the table above.
So, if the URL to your file server is `http://example.com/`, your
`~/hello-docs/site` folder containing the documentation should be accessible at
`http://example.com/hello-docs/`.
## Configure TechDocs to read from file server
In order for Backstage to show your documentation, it needs to know where you
uploaded it.
Make sure you have Backstage set up using
[TechDocs getting started](getting-started.md)
To point Backstage to your docs storage, add or change the following lines in
your Backstage `app-config.yaml`:
```yaml
techdocs:
storageUrl: http://example.com
```
You can now start Backstage using `yarn start` and open up your browser at
`http://localhost:3000/docs/hello-docs` to view your docs.
-1
View File
@@ -1 +0,0 @@
# Debugging
+108
View File
@@ -1 +1,109 @@
# Getting Started
> TechDocs is not feature complete and currently you can't set up a complete
> end-to-end working TechDocs plugin without customizing the plugin itself.
> With TechDocs V.0 you can expect a demonstration of how to integrate docs into
> Backstage. Currently it can create docs using
> [mkdocs](https://www.mkdocs.org/), as well as reading published docs. If you
> publish generated docs and passing in a storageUrl in your `app-config.yaml`
> you can view it in Backstage by going to
> `http://localhost:3000/docs/<remote-folder>`.
Getting started with TechDocs is easy. TechDocs functions as a plugin to
Backstage, why you will need to use Backstage to use TechDocs.
## What is Backstage?
Backstage is an open platform for building developer portals. Its based on the
developer portal weve been using internally at Spotify for over four years.
[Read more](https://github.com/spotify/backstage).
## Prerequisities
In order to use Backstage and TechDocs, you will need to have the following
installed:
- [Node.js](https://nodejs.org) Active LTS (long term support), currently v12
- [Yarn](https://yarnpkg.com/getting-started/install)
## Creating a new Backstage app
> If you have already created a Backstage application for this purpose, jump to
> [Installing TechDocs](#installing-techdocs), otherwise complete this step.
To create a new Backstage application for us to set up TechDocs, you will need
to run the following command:
```bash
npx @backstage/cli create-app
```
You will then be prompted to enter a name for your application. Once you do so,
this will create a new Backstage application for you in a new folder. For
example, if we chose the name `hello-world` for our application, it would create
a new `hello-world` folder containing our new Backstage application.
## Installing TechDocs
Inside of our new Backstage application, TechDocs is not provided by default.
For this reason we will need to manually set up TechDocs. It should take less
than a minute.
### Adding the package
We will need to add our plugin to your Backstage application. To do so, you can
navigate to your new Backstage application folder and then run a single command
to install TechDocs.
```bash
cd hello-world/
```
Then you need to navigate to your `packages/app` folder to install TechDocs:
```bash
cd packages/app
yarn add @backstage/plugin-techdocs
```
After a short while, it should successfully install the TechDocs plugin. Now we
just need to set up some basic configuration!
Enter the following command:
```bash
yarn install
```
Add this to `packages/app/src/plugins.ts`:
```typescript
export { plugin as TechDocs } from '@backstage/plugin-techdocs';
```
### Setting the configuration
TechDocs allows for configuration of the docs storage URL through your
app-config file. The URL provided here is demo docs used to testing.
To use the demo docs, add the following lines to `app-config.yaml`:
```yaml
techdocs:
storageUrl: https://techdocs-mock-sites.storage.googleapis.com
```
## Run Backstage Locally
Change folder to your Backstage application root.
```bash
yarn start
```
Open browser at [http://localhost:3000/docs/](http://localhost:3000/docs/)
## Extra Reading
[Back to Docs](README.md)
@@ -1 +0,0 @@
# Publishing Documentation
@@ -1 +0,0 @@
# Reading Documentation
@@ -1 +0,0 @@
# Writing Documentation

Before

Width:  |  Height:  |  Size: 303 KiB

After

Width:  |  Height:  |  Size: 303 KiB

+27
View File
@@ -0,0 +1,27 @@
# Deployment (Other)
## Deploying Locally
### Try on Docker
Run the following commands if you have Docker environment
```bash
$ yarn docker-build
$ docker run --rm -it -p 80:80 spotify/backstage
```
Then open http://localhost/ on your browser.
### Running with `docker-compose`
Run the following commands if you have docker and docker-compose for a full
example, with the example backend also deployed.
```bash
$ yarn docker-build:all
$ docker-compose up
```
Then open http://localhost:3000 on your browser to see the example app with an
example backend.
@@ -50,7 +50,8 @@ system resources and slow things down.
## Package Scripts
There are many commands to be found in the root [package.json](package.json),
There are many commands to be found in the root
[package.json](https://github.com/spotify/backstage/blob/master/package.json),
here are some useful ones:
```python
@@ -73,26 +74,15 @@ yarn test:all # test all packages
yarn clean # Remove all output folders and @backstage/cli cache
yarn bundle # Build a production bundle of the example app
yarn diff # Make sure all plugins are up to date with the latest plugin template
yarn create-plugin # Create a new plugin
```
### (Optional)Try on Docker
> See
> [package.json](https://github.com/spotify/backstage/blob/master/package.json)
> for other yarn commands/options.
Run the following commands if you have Docker environment
[Next Step - Create a Backstage plugin](../plugins/create-a-plugin.md)
```bash
$ yarn docker-build
$ docker run --rm -it -p 80:80 spotify/backstage
```
Then open http://localhost/ on your browser.
> See [package.json](/package.json) for other yarn commands/options.
[Next Step - Create a Backstage plugin](create-a-plugin.md)
[Back to Docs](README.md)
[Back to Docs](../README.md)
@@ -62,9 +62,8 @@ collection of tutorials that will guide you through setting up and extending an
instance of Backstage with your own plugins.
- [Development Environment](development-environment.md)
- [Create a Backstage Plugin](create-a-plugin.md)
- [Structure of a Plugin](structure-of-a-plugin.md)
- [Utility APIs](utility-apis.md)
- Using Backstage components (TODO)
- [Create a Backstage Plugin](../plugins/create-a-plugin.md)
- [Structure of a Plugin](../plugins/structure-of-a-plugin.md)
- [Utility APIs](../api/utility-apis.md)
[Back to Docs](../README.md)
+190
View File
@@ -0,0 +1,190 @@
# Typical Backstage architecture
The following diagram shows how Backstage might look when deployed inside a
company which uses the Tech Radar plugin, the Lighthouse plugin, the Circle CI
plugin and the service catalog.
There are 3 main components in this architecture:
1. The core Backstage UI
2. The UI plugins and their backing services
3. Databases
Running this architecture in a real environment typically involves
containerising the components. Various commands are provided for accomplishing
this.
![The architecture of a basic Backstage application](./architecture-overview/backstage-typical-architecture.png)
# The UI
The UI is a thin, client-side wrapper around a set of plugins. It provides some
core UI components and libraries for shared activities such as config
management. [[live demo](https://backstage-demo.roadie.io/)]
![UI with different components highlighted](./architecture-overview/core-vs-plugin-components-highlighted.png)
Each plugin typically makes itself available in the UI on a dedicated URL. For
example, the lighthouse plugin is registered with the UI on `/lighthouse`.
[[live demo](https://backstage-demo.roadie.io/lighthouse)]
![The lighthouse plugin UI](./architecture-overview/lighthouse-plugin.png)
The Circle CI plugin is available on `/circleci`.
![Circle CI Plugin UI](./architecture-overview/circle-ci.png)
# Plugins and plugin backends
Each plugin is a client side application which mounts itself on the UI. Plugins
are written in TypeScript or JavaScript. They each live in their own directory
in `backstage/plugins`. For example, the source code for the lighthouse plugin
is available at
[backstage/plugins/lighthouse](https://github.com/spotify/backstage/tree/master/plugins/lighthouse).
## Installing plugins
Plugins are typically loaded by the UI in your Backstage applications
`plugins.ts` file. For example,
[here](https://github.com/spotify/backstage/blob/master/packages/app/src/plugins.ts)
is that file in the Backstage sample app.
Plugins can be enabled, and passed configuration in `apis.ts`. For example,
[here](https://github.com/spotify/backstage/blob/master/packages/app/src/apis.ts)
is that file in the Backstage sample app.
This is how the lighthouse plugin would be enabled in a typical Backstage
application:
```tsx
import { ApiHolder, ApiRegistry } from '@backstage/core';
import {
lighthouseApiRef,
LighthouseRestApi,
} from '@backstage/plugin-lighthouse';
const builder = ApiRegistry.builder();
export const lighthouseApi = new LighthouseRestApi(/* URL of the lighthouse microservice! */);
builder.add(lighthouseApiRef, lighthouseApi);
export default builder.build() as ApiHolder;
```
As of this moment, there is no config based install procedure for plugins. Some
code changes are required.
## Plugin architecture
Architecturally, plugins can take three forms:
1. Standalone
2. Service backed
3. Third-party backed
### Standalone plugins
Standalone plugins run entirely in the browser.
[The tech radar plugin](https://backstage-demo.roadie.io/tech-radar), for
example, simply renders hard-coded information. It doesn't make any API requests
to other services.
![tech radar plugin ui](./architecture-overview/tech-radar-plugin.png)
The architecture of the Tech Radar installed into a Backstage app is very
simple.
![ui and tech radar plugin connected together](./architecture-overview/tech-radar-plugin-architecture.png)
### Service backed plugins
Service backed plugins make API requests to a service which is within the
purview of the organisation running Backstage.
The lighthouse plugin, for example, makes requests to the
[lighthouse-audit-service](https://github.com/spotify/lighthouse-audit-service).
The lighthouse-audit-service is a microservice which runs a copy of Google's
[Lighthouse library](https://github.com/GoogleChrome/lighthouse/) and stores the
results in a PostgreSQL database.
Its architecture looks like this:
![lighthouse plugin backed to microservice and database](./architecture-overview/lighthouse-plugin-architecture.png)
The service catalog in Backstage is another example of a service backed plugin.
It retrieves a list of services, or "entities", from the Backstage Backend
service and renders them in a table for the user.
### Third-party backed plugins
Third-party backed plugins are similar to service backed plugins. The main
difference is that the service which backs the plugin is hosted outside of the
ecosystem of the company hosting Backstage.
The Circle CI plugin is an example of a third-party backed plugin. Circle CI is
a SaaS service which can be used without any knowledge of Backstage. It has an
API which a Backstage plugin consumes to display content.
Requests which go to Circle CI from the users browser are passed through a proxy
service that Backstage provides. Without this, the requests would be blocked by
Cross Origin Resource Sharing policies which prevent a browser page served at
[https://example.com](https://example.com) from serving resources hosted at
https://circleci.com.
![CircleCi plugin talking to proxy talking to SaaS Circle CI](./architecture-overview/circle-ci-plugin-architecture.png)
# Databases
As we have seen, both the lighthouse-audit-service and catalog-backend require a
database to work with.
At the time of writing, the lighthouse-audit-service requires PostgreSQL to work
with. The service catalog backend uses an in-memory Sqlite3 instance. This is a
development oriented setup and there are plans to support other databases in the
future.
To learn more about the future of databases and Backstage, see the following two
GitHub issues.
[Knex + Plugins (Multiple vs Single Database) · Issue #1598 · spotify/backstage](https://github.com/spotify/backstage/issues/1598)
[Update migrations to support postgres by dariddler · Pull Request #1527 · spotify/backstage](https://github.com/spotify/backstage/pull/1527#discussion_r450374145)
# Containerization
The example Backstage architecture shown above would Dockerize into three
separate docker images.
1. The frontend container
2. The backend container
3. The lighthouse audit service container
![Boxes around the architecture to indicate how it is containerised](./architecture-overview/containerised.png)
The frontend container can be built with a provided command.
```bash
yarn install
yarn tsc
yarn build
yarn run docker-build
```
Running this will simply generate a Docker container containing the contents of
the UIs `dist` directory. The resulting container will be about 50MB in size.
The backend container can be built by running the following command in the
`packages/backend` directory.
```bash
yarn run build-image
```
This will create a ~500MB container called `example-backend`.
The lighthouse-audit-service container is already publicly available in Docker
Hub and can be downloaded and ran with
```bash
docker run spotify/lighthouse-audit-service:latest
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

@@ -14,5 +14,3 @@ different ways.
Spotify we have over 100 plugins built by over 50 different teams. It has been
very powerful to get contributions from various infrastructure teams added
into a single unified developer experience.
[Back to Docs](README.md)
+36
View File
@@ -0,0 +1,36 @@
# 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:
- 🐣 **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.
- 🐢 **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.
- 🐇 **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.
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.
Our vision for Backstage is for it to become the trusted standard toolbox (read:
UX layer) for the open source infrastructure landscape. Think of it like
Kubernetes for developer experience. We realize this is an ambitious goal. We
cant do it alone. If this sounds interesting or you'd like to help us shape our
product vision, we'd love to talk. You can email me directly:
[alund@spotify.com](mailto:alund@spotify.com).
+18
View File
@@ -0,0 +1,18 @@
# Support and 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](../FAQ.md) - Frequently Asked Questions
- [Code of Conduct](../../CODE_OF_CONDUCT.md) - This is how we roll
- [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 ❤️
Or, if you are an open source developer and are interested in joining our team,
please reach out to
[foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com)
+44
View File
@@ -0,0 +1,44 @@
# [Backstage](https://backstage.io)
![headline](../headline.png)
## What is Backstage?
[Backstage](https://backstage.io/) is an open platform for building developer
portals. Its based on the developer portal weve been using internally at
Spotify for over four years. Backstage can be as simple as a services catalog or
as powerful as the UX layer for your entire tech infrastructure.
For more information go to [backstage.io](https://backstage.io) or join our
[Discord chatroom](https://discord.gg/EBHEGzX).
### Features
- Create and manage all of your organizations software and microservices in one
place.
- Services catalog keeps track of all software and its ownership.
- Visualizations provide information about your backend services and tooling,
and help you monitor them.
- A unified method for managing microservices offers both visibility and
control.
- Preset templates allow engineers to quickly create microservices in a
standardized way
([coming soon](https://github.com/spotify/backstage/milestone/11)).
- Centralized, full-featured technical documentation with integrated tooling
that makes it easy for developers to set up, publish, and maintain alongside
their code ([coming soon](https://github.com/spotify/backstage/milestone/15)).
### Benefits
- For _engineering managers_, it allows you to maintain standards and best
practices across the organization, and can help you manage your whole tech
ecosystem, from migrations to test certification.
- For _end users_ (developers), it makes it fast and simple to build software
components in a standardized way, and it provides a central place to manage
all projects and documentation.
- For _platform engineers_, it enables extensibility and scalability by letting
you easily integrate new tools and services (via plugins), as well as
extending the functionality of existing ones.
- For _everyone_, its a single, consistent experience that ties all your
infrastructure tooling, resources, standards, owners, contributors, and
administrators together in one place.
View File
View File
@@ -24,7 +24,7 @@ for your new plugin directly by navigating to
`http://localhost:3000/my-plugin`._
<p align='center'>
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/my-plugin_screenshot.png' width='600' alt='my plugin'>
<img src='https://github.com/spotify/backstage/raw/master/docs/plugins/my-plugin_screenshot.png' width='600' alt='my plugin'>
</p>
You can also serve the plugin in isolation by running `yarn start` in the plugin
@@ -40,4 +40,4 @@ for it can be found inside the plugin's `dev/` directory.
[Next Step - Structure of a plugin](structure-of-a-plugin.md)
[Back to Getting Started](README.md)
[Back to Getting Started](../README.md)
+11
View File
@@ -0,0 +1,11 @@
# Existing plugins
## Open source plugins
The full list of open source plugins can be found
[here](https://github.com/spotify/backstage/tree/master/plugins).
## Plugin gallery
TODO: In the future we would like to have something similar to
https://grafana.com/grafana/plugins
+25
View File
@@ -0,0 +1,25 @@
# Plugins
Backstage is a single-page application composed of a set of plugins.
Our goal for the plugin ecosystem is that the definition of a plugin is flexible
enough to allow you to expose pretty much any kind of infrastructure or software
development tool as a plugin in Backstage. By following strong
[design guidelines](../dls/design.md) we ensure the the overall user experience
stays consistent between plugins.
![plugin](my-plugin_screenshot.png)
## Creating a plugin
To create a plugin, follow the steps outlined [here](create-a-plugin.md).
## Suggesting a plugin
If you start developing a plugin that you aim to release as open source, we
suggest that you create a new
[new Issue](https://github.com/spotify/backstage/issues/new?template=plugin_template.md).
This helps the community know what plugins are in development.
You can also use this process if you have an idea for a good plugin but you hope
that someone else will pick up the work.

Before

Width:  |  Height:  |  Size: 389 KiB

After

Width:  |  Height:  |  Size: 389 KiB

View File
View File
@@ -3,9 +3,10 @@
## NPM
NPM packages are published through CI/CD in the
[.github/workflows/master.yml](../.github/workflows/master.yml) workflow. Every
commit that is merged to master will be checked for new versions of all public
packages, and any new versions will automatically be published to NPM.
[.github/workflows/master.yml](https://github.com/spotify/backstage/blob/master/.github/workflows/master.yml)
workflow. Every commit that is merged to master will be checked for new versions
of all public packages, and any new versions will automatically be published to
NPM.
### Creating a new release
@@ -35,4 +36,4 @@ $ git push origin -u new-release
And then create a PR. Once the PR is approved and merged into master, the master
build will publish new versions of all bumped packages.
[Back to Docs](README.md)
[Back to Docs](../README.md)
@@ -102,6 +102,6 @@ environment you will probably face challenges like CORS policies and/or
backend-side authorization. To smooth this process out you can use proxy -
either the one you already have (like nginx/haproxy/etc) or the proxy-backend
plugin that we provide for the backstage backend.
[Read more](../../plugins/proxy-backend/README.md)
[Read more](https://github.com/spotify/backstage/blob/master/plugins/proxy-backend/README.md)
[Back to Getting Started](README.md)
[Back to Getting Started](../README.md)
+371
View File
@@ -0,0 +1,371 @@
# Testing with Jest
Backstage uses [Jest](https://facebook.github.io/jest/) for all our unit testing
needs.
Jest is a Facebook-built unit testing framework specifically built for React. It
follows in the footsteps of other classic Node.js unit testing-related
frameworks and libraries like [Mocha](https://mochajs.org/),
[Jasmine](https://jasmine.github.io/), and [Chai](http://www.chaijs.com/).
## Running Tests
Running all tests:
yarn test-react
Running an individual test (e.g. `MyComponent.test.js`):
yarn test-react MyComponent
To run both `MyComponent.test.js` and `MyControl.test.js` suite of tests:
yarn test-react MyCo
Note: if `console.logs` are not appearing, run only the individual test you are
working on.
[This is a bug in Jest](https://github.com/facebook/jest/issues/2441).
## Naming Test Files
Tests should be name `[filename].test.js`.
For example, the tests for **`Link.js`** exist in the file **`Link.test.js`**.
## Third-Party Dependencies
Jest has its own built-in assertion library with `expect`, so there is no need
to `import` a third-party library like some of the older frameworks required.
However since assertion libraries simply throw errors, it would be feasible to
import a third-party library if you needed (like Chai or
[Sinon](http://sinonjs.org/)).
We use the light-weight
[react-testing-library](https://github.com/kentcdodds/react-testing-library) to
render React components.
## Testing Utilities
TODO.
# Writing Unit Tests
The following principles are good guides to determining if you are writing high
quality frontend unit tests.
## Bad Unit Test Principle
> No unit test is better than a bad one.
Writing a poor unit test:
- Gives the illusion your code is more secure or reliable than it actually is.
- Functions equivalent to a bad comment, in that it leads the next developer
into erroneous assumptions.
- Adds to future work by requiring updates to the unit test for irrelevant code
changes.
## Input/Output Principle
> A unit test verifies an output matches an expected input.
For backend, this would be that when you provide configuration X, then the
object responds with Y. For frontend, this would be that when you provide
properties X to a component, then the visual functionality responds with Y.
## Blackbox Principle
> A good unit test does not tell the object how it should do its job but should
> only compare inputs to outputs.
Consider a unit test for a form. A good unit test would not test the order of
the form fields. Instead, it would verify that the inputs to the form fields
lead to a certain backend call when submit is clicked.
## Scalability Principle
> Unit test quality is directly proportionate to how much code can change
> without having to touch the unit test.
This is often overlooked! A unit test is not a test to verify the code never
changes. Poor unit tests are written so that every time you make a tiny change
to the code, you have to update the unit test. A good unit test suite allows a
lot of flexibility in _how_ the code is written so that future refactoring can
occur without having to touch the original unit tests.
## Increasing Complexity Principle
> The ordering of unit tests in a suite should proceed from least specific to
> most specific.
Jest runs all tests in the order in which they are provided, regardless of the
depth of `describe()` blocks you provide. We can use this to help us write tests
that will help the next developer debug what they broke.
The idea here is that if they were to break a unit test, the next developer
should be able to tell from the order in which the tests broke what they should
do to fix things.
For example, good unit tests will verify the arguments to a function in a test
prior to a test that validates the output. If you do not test this, then simply
throwing an error saying that output was incorrect will lead the next developer
into thinking they may have broken the entire functionality of the object rather
than simply letting them know they had an invalid input.
## Broken Functionality Principle
> Generally, a unit test should not test exactly how the output appears, it
> should test that the functionality has an expected _general_ response to an
> input change.
This piggybacks the Scalability Principle and applies primarily to frontend
development. As a general rule of thumb, frontends should be flexible enough so
that the UX or design can change while touching the least amount of code
possible. So for example, a poor unit test would verify the color of a button
when it is hovered. This would be a poor unit test, because if you decide to
test a slightly different color on the button the unit test will break. A better
unit test would verify that the button's CSS classname is assigned properly on
hover or test for something completely different.
## Example: Loading Indicator
A classic unit test on frontends is verifying a loading indicator displays when
a backend request is being made.
**Here are some things we could test for _when data is loading_:**
> Did the internal `loading` state of the component change? (poor)
This is not a great test because it does not actually test that the
functionality (displaying a message to the user) actually happens. It also
breaks the Blackbox Principle by expecting the internals of the component must
work a certain way. This could be a good test on its own right, but it does not
actually achieve the goal of verifying that if our input (loading data) occurs,
then the output (displaying a message to the user) has happened.
> Did the text `"Loading!"` appear in the DOM? (better)
This is a better test because it validates functionality, but it breaks the
Scalability principle. By testing for `'Loading...'` we are linking our test
code to the component's message. If we want to add internationalization or
simply change the message to something more specific we will break our test and
have to update code in two places.
> Did `<Loading />` get mounted? (best)
This is the best test of these examples (there could be more depending on your
implementation).
Verifying that `<Loading />` is mounted when data is loading is the best test
because it fulfills all the principles above:
**Fulfills Input/Output Principle**: Verifies the output changes when the
input changes
**Fufills Blackbox Principle**: Does not verify _how_ the `<Loading />`
component is mounted, just that it is mounted in response to the input.
**Fulfills Scalability Principle**: If we decide to refactor the entire way
the loading indicator is displayed the test still works without touching it.
**Fulfills Broken Functionality Principle**: this test verifies the
functionality (displaying an indicator) is working, rather than how it is
working.
The increasing complexity principle does not really apply to this example, so it
was excluded. However if you were to place this test in a suite of other tests,
it would be best to test first that when the component is instructed to load
data then it actually does it. this way both tests fail if the data loading part
breaks and the next developer immediately know the problem is that the data
loading is broken, not that the loading indicator is broken.
# Examples
## Utility Functions
A utility function is a function with no side effects. It takes in arguments and
returns a result or displays an error or console message, like so:
**`StringUtil ellipsis`**
export function ellipsis(text, maxLength, midCharIx = 0, ellipsis = '...') {
// Do something blackbox. We should not care about the internals, only inputs and outputs.
...
return someFinalValue;
}
There are four things to test for in a utility function:
1. Handle Invalid Input
2. Verify default input arguments
3. Verify output for expected input arguments
4. Handle thrown errors
> Handle Invalid Input (handle thrown errors):
it('Throws an error on improper arguments', () => {
expect(() => {
ellipsis();
}).toThrowError('Expected \'text\' to be defined');
});
> Verify default input arguments:
it('Works with defaults', () => {
expect(ellipsis('Hello world', 3)).toBe('Hel...');
expect(ellipsis('', 3)).toBe('');
expect(ellipsis('H', 3)).toBe('H');
expect(ellipsis('Hello', 5)).toBe('Hello');
});
> Verify output for expected input arguments:
This is especially true for edge cases!
it('Works with midCharIx', () => {
expect(ellipsis('Hello world', 3, 6)).toBe('...o w...');
expect(ellipsis('', 3, 6)).toBe('');
expect(ellipsis('Backstage is amazing', 4, 10)).toBe('...e is...');
});
## Non-React Classes
Testing a Javascript object which is _not_ a React component follows a lot of
the same principles as testing objects in other languages.
### API Testing Principles
Testing an API involves verifying four things:
1. Invalid inputs are caught before being sent to the server.
2. Valid inputs translate into a valid browser request.
3. Server response is translated into an expected Javascript object.
4. Server errors are handled gracefully.
### Mocking API Calls
[Mocking in jest](https://facebook.github.io/jest/docs/en/mock-functions.html)
involves wrapping existing functions (like an API call function) with an
alternative.
For example:
**./Api.js**
```
export {
fetchSomethingFromServer: () => {
// Live production call to a URI. Must be avoided during testing!
return fetch('blah');
}
};
```
**./\_\_mocks\_\_/Api.js**
```
export {
fetchSomethingFromServer: () => {
// Simulate a production call, but avoid jest and just use a promise
return Promise.resolve('some result object simulating server data here');
}
}
```
**./Api.test.js**
```
/* eslint-disable import/first */
jest.mock('./MyApi'); // Instruct Jest to swap all future imports of './MyApi.js' to './__mocks__/MyApi.js'
import MyApi from './MyApi'; // Will actually return the contents of the file in the __mocks__ folder now
it ('loads data', (done) => {
MyApi.fetchSomethingFromServer().then(result => {
expect(result).toBe('some result object simulating server data here');
done();
});
});
```
Note: make sure you disable the eslint `'import/first'` rule at the top of the
file since technically you are not allowed by the default settings to have an
import after the `jest.mock` call.
## React Components
### Working with the React Lifecycle
The [React lifecycle](https://reactjs.org/docs/state-and-lifecycle.html) is
asynchronous.
When you call `setState` or update the `props` of a component, there are several
asynchronous stages that must occur before a rerender. Note the following
example:
```jsx
class MyComponent extends Component {
load() {
this.setState({loading: true});
}
render() {
return this.state.loading ? <Loading /> : 'Finished!';
}
}
...
// INCORRECT
it('Test loading', () => {
const wrapper = mount(<MyComponent />);
wrapper.load();
expect(wrapper.find('Loading').length).toEqual(1); // Will fail
});
// CORRECT
it('Test loading', () => {
const wrapper = mount(<MyComponent />);
wrapper.load();
wrapper.update(); // This tells the components to run through a render cycle
expect(wrapper.find('Loading').length).toEqual(1);
});
```
For more information:
- [React lifecycle](https://reactjs.org/docs/state-and-lifecycle.html)
### Accessing `store`, `theme`, routing, browser history, etc.
The Backstage application has several core providers at its root. To run your
test wrapped in a "dummy" Backstage application, you can use our utility
functions:
**`wrapInTestApp`**
import { wrapInTestApp } from '../../test-utils';
...
it('Definitely is not a coconut', () => {
const mangoWrapper = mount(wrapInTestApp(<Mango />));
expect(mangoWrapper.context().store).toBeDefined();
});
Note: wrapping in the test application **requires** you to do a `find()` or
`dive()` since the wrapped component is now the application.
# Debugging Jest Tests
Currently, debugging Jest tests using IntelliJ or `node-debugger` is possible
but can be
[problematic to set up.](https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000634564-Debugging-Jest-unit-tests)
It is possible, but you might spend a decent amount of time configuring your
IDE.
In most cases, we have found that using `console.log` works well.
Note: if your console.logs are not being displayed, focus your specific unit
test from the command line by running them like so `yarn test-react MyTest`.
-9
View File
@@ -1,9 +0,0 @@
# Reference documentation
APIs and Components
- [createPlugin](createPlugin.md)
- [createPlugin - router](createPlugin-router.md)
- [createPlugin - feature flags](createPlugin-feature-flags.md)
[Back to Docs](../README.md)
@@ -64,5 +64,3 @@ const ExampleButton: FC<{}> = () => {
);
};
```
[Back to References](README.md)
+1 -1
View File
@@ -35,4 +35,4 @@ const myPluginRouteRef = createRouteRef({
});
```
[Back to References](README.md)
[Back to References](../README.md)
-2
View File
@@ -39,5 +39,3 @@ export default createPlugin({
},
});
```
[Back to References](README.md)
+114
View File
@@ -0,0 +1,114 @@
# AlertApi
The AlertApi type is defined at
[packages/core-api/src/apis/definitions/AlertApi.ts:29](https://github.com/spotify/backstage/blob/53a229ea7576b1432835e54e41e0b9526038afa4/packages/core-api/src/apis/definitions/AlertApi.ts#L29).
The following Utility API implements this type: [alertApiRef](./README.md#alert)
## Members
### post()
Post an alert for handling by the application.
<pre>
post(alert: <a href="#alertmessage">AlertMessage</a>): void
</pre>
### alert\$()
Observe alerts posted by other parts of the application.
<pre>
alert$(): <a href="#observable">Observable</a>&lt;<a href="#alertmessage">AlertMessage</a>&gt;
</pre>
## Supporting types
These types are part of the API declaration, but may not be unique to this API.
### AlertMessage
<pre>
export type AlertMessage = {
message: string;
// Severity will default to success since that is what material ui defaults the value to.
severity?: 'success' | 'info' | 'warning' | 'error';
}
</pre>
Defined at
[packages/core-api/src/apis/definitions/AlertApi.ts:19](https://github.com/spotify/backstage/blob/53a229ea7576b1432835e54e41e0b9526038afa4/packages/core-api/src/apis/definitions/AlertApi.ts#L19).
Referenced by: [post](#post), [alert\$](#alert).
### Observable
Observable sequence of values and errors, see TC39.
https://github.com/tc39/proposal-observable
This is used as a common return type for observable values and can be created
using many different observable implementations, such as zen-observable or
RxJS 5.
<pre>
export type Observable&lt;T&gt; = {
/**
* Subscribes to this observable to start receiving new values.
*/
subscribe(observer: <a href="#observer">Observer</a>&lt;T&gt;): <a href="#subscription">Subscription</a>;
subscribe(
onNext: (value: T) =&gt; void,
onError?: (error: Error) =&gt; void,
onComplete?: () =&gt; void,
): <a href="#subscription">Subscription</a>;
}
</pre>
Defined at
[packages/core-api/src/types.ts:53](https://github.com/spotify/backstage/blob/53a229ea7576b1432835e54e41e0b9526038afa4/packages/core-api/src/types.ts#L53).
Referenced by: [alert\$](#alert).
### Observer
This file contains non-react related core types used throught Backstage.
Observer interface for consuming an Observer, see TC39.
<pre>
export type Observer&lt;T&gt; = {
next?(value: T): void;
error?(error: Error): void;
complete?(): void;
}
</pre>
Defined at
[packages/core-api/src/types.ts:24](https://github.com/spotify/backstage/blob/53a229ea7576b1432835e54e41e0b9526038afa4/packages/core-api/src/types.ts#L24).
Referenced by: [Observable](#observable).
### Subscription
Subscription returned when subscribing to an Observable, see TC39.
<pre>
export type Subscription = {
/**
* Cancels the subscription
*/
unsubscribe(): void;
/**
* Value indicating whether the subscription is closed.
*/
readonly closed: Boolean;
}
</pre>
Defined at
[packages/core-api/src/types.ts:33](https://github.com/spotify/backstage/blob/53a229ea7576b1432835e54e41e0b9526038afa4/packages/core-api/src/types.ts#L33).
Referenced by: [Observable](#observable).

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