Merge branch 'master' into ebarriosjr/Creating-gcp-projects-plugin
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Master Build TechDocs PyPI Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- '.github/workflows/techdocs-pypi.yml'
|
||||
- 'packages/techdocs-container/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python-version: [3.7]
|
||||
|
||||
steps:
|
||||
# Publish techdocs-core to PyPI
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@master
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Build Python distribution
|
||||
working-directory: ./packages/techdocs-container/techdocs-core
|
||||
run: |
|
||||
pip install wheel
|
||||
rm -rf dist
|
||||
python setup.py bdist_wheel sdist --formats gztar
|
||||
- name: Publish a Python distribution to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_KEY }}
|
||||
packages_dir: ./packages/techdocs-container/techdocs-core/dist
|
||||
@@ -33,12 +33,12 @@ jobs:
|
||||
push: false
|
||||
|
||||
# Lint Python code for techdocs-core package
|
||||
- name: prepare python environment
|
||||
- name: Prepare Python environment
|
||||
run: |
|
||||
python3 -m pip install --index-url https://pypi.org/simple/ setuptools
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install --index-url https://pypi.org/simple/ -r $TECHDOCS_CORE_PATH/requirements.txt
|
||||
|
||||
- name: lint techdocs-core package
|
||||
- name: Lint techdocs-core package
|
||||
run: |
|
||||
python3 -m black --check $TECHDOCS_CORE_PATH/src
|
||||
|
||||
@@ -122,3 +122,6 @@ dist
|
||||
|
||||
# Temporary change files created by Vim
|
||||
*.swp
|
||||
|
||||
# MkDocs build output
|
||||
site
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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) 👏🏻💯
|
||||
|
||||
@@ -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 ❤️
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
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`
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
We have also found that the service catalog is a great way to organise the
|
||||
infrastructure tools you use to manage the software as well. This is how
|
||||
Backstage creates one developer portal for all your tools. Rather than asking
|
||||
teams to jump between different infrastructure UI’s (and incurring additional
|
||||
cognitive overhead each time they make a context switch), most of these tools
|
||||
can be organised around the entities in the catalog.
|
||||
|
||||
## Using the Service Catalog
|
||||
|
||||
TODO
|
||||
|
||||

|
||||
|
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
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
@@ -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)
|
||||
@@ -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:
|
||||
|
||||

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

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

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

|
||||
|
||||
It shouldn't take too long, and you'll have a success screen!
|
||||
|
||||

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

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

|
||||
|
||||
And then you'll also be able to see it in the Catalog View table
|
||||
|
||||

|
||||
@@ -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
|
||||
|
||||
@@ -2,17 +2,41 @@
|
||||
|
||||
## What is it?
|
||||
|
||||
Intro, backstory, etc.
|
||||
<!-- Intro, backstory, etc.: -->
|
||||
|
||||
Wait, what is TechDocs? TechDocs is Spotify’s homegrown docs-like-code solution
|
||||
built directly into Backstage. Today, it is now one of the core products in
|
||||
Spotify’s 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
|
||||
|
||||
@@ -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 +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 +0,0 @@
|
||||
# Debugging
|
||||
@@ -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. It’s based on the
|
||||
developer portal we’ve 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 |
@@ -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)
|
||||
@@ -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 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/)]
|
||||
|
||||

|
||||
|
||||
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 Circle CI plugin is available on `/circleci`.
|
||||
|
||||

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

|
||||
|
||||
The architecture of the Tech Radar installed into a Backstage app is very
|
||||
simple.
|
||||
|
||||

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

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

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

|
||||
|
||||
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
|
||||
```
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 293 KiB |
@@ -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)
|
||||
@@ -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
|
||||
can’t do it alone. If this sounds interesting or you'd like to help us shape our
|
||||
product vision, we'd love to talk. You can email me directly:
|
||||
[alund@spotify.com](mailto:alund@spotify.com).
|
||||
@@ -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)
|
||||
@@ -0,0 +1,44 @@
|
||||
# [Backstage](https://backstage.io)
|
||||
|
||||

|
||||
|
||||
## What is Backstage?
|
||||
|
||||
[Backstage](https://backstage.io/) is an open platform for building developer
|
||||
portals. It’s based on the developer portal we’ve been using internally at
|
||||
Spotify for over four years. Backstage can be as simple as a services catalog or
|
||||
as powerful as the UX layer for your entire tech infrastructure.
|
||||
|
||||
For more information go to [backstage.io](https://backstage.io) or join our
|
||||
[Discord chatroom](https://discord.gg/EBHEGzX).
|
||||
|
||||
### Features
|
||||
|
||||
- Create and manage all of your organization’s software and microservices in one
|
||||
place.
|
||||
- Services catalog keeps track of all software and its ownership.
|
||||
- Visualizations provide information about your backend services and tooling,
|
||||
and help you monitor them.
|
||||
- A unified method for managing microservices offers both visibility and
|
||||
control.
|
||||
- Preset templates allow engineers to quickly create microservices in a
|
||||
standardized way
|
||||
([coming soon](https://github.com/spotify/backstage/milestone/11)).
|
||||
- Centralized, full-featured technical documentation with integrated tooling
|
||||
that makes it easy for developers to set up, publish, and maintain alongside
|
||||
their code ([coming soon](https://github.com/spotify/backstage/milestone/15)).
|
||||
|
||||
### Benefits
|
||||
|
||||
- For _engineering managers_, it allows you to maintain standards and best
|
||||
practices across the organization, and can help you manage your whole tech
|
||||
ecosystem, from migrations to test certification.
|
||||
- For _end users_ (developers), it makes it fast and simple to build software
|
||||
components in a standardized way, and it provides a central place to manage
|
||||
all projects and documentation.
|
||||
- For _platform engineers_, it enables extensibility and scalability by letting
|
||||
you easily integrate new tools and services (via plugins), as well as
|
||||
extending the functionality of existing ones.
|
||||
- For _everyone_, it’s a single, consistent experience that ties all your
|
||||
infrastructure tooling, resources, standards, owners, contributors, and
|
||||
administrators together in one place.
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
## 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 |
@@ -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)
|
||||
@@ -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`.
|
||||
@@ -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)
|
||||
|
||||
@@ -35,4 +35,4 @@ const myPluginRouteRef = createRouteRef({
|
||||
});
|
||||
```
|
||||
|
||||
[Back to References](README.md)
|
||||
[Back to References](../README.md)
|
||||
|
||||
@@ -39,5 +39,3 @@ export default createPlugin({
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
[Back to References](README.md)
|
||||
|
||||
@@ -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><<a href="#alertmessage">AlertMessage</a>>
|
||||
</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<T> = {
|
||||
/**
|
||||
* Subscribes to this observable to start receiving new values.
|
||||
*/
|
||||
subscribe(observer: <a href="#observer">Observer</a><T>): <a href="#subscription">Subscription</a>;
|
||||
subscribe(
|
||||
onNext: (value: T) => void,
|
||||
onError?: (error: Error) => void,
|
||||
onComplete?: () => 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<T> = {
|
||||
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).
|
||||