Merge branch 'master' into rugvip/docgen
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
| 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. |
|
||||
| [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 |
|
||||
@@ -8,3 +8,6 @@
|
||||
| [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
|
||||
|
||||
@@ -15,10 +15,13 @@ 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
|
||||
|
||||
@@ -22,7 +22,7 @@ function inject_config() {
|
||||
>&2 echo "Runtime app config: $config"
|
||||
|
||||
local main_js
|
||||
if ! main_js="$(grep -l __APP_INJECTED_RUNTIME_CONFIG__ /usr/share/nginx/html/*.chunk.js)"; then
|
||||
if ! main_js="$(grep -l __APP_INJECTED_RUNTIME_CONFIG__ /usr/share/nginx/html/static/*.js)"; then
|
||||
echo "Runtime config already written"
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -6,6 +6,7 @@ better yet, a pull request.
|
||||
|
||||
- Overview
|
||||
- [What is Backstage?](overview/what-is-backstage.md)
|
||||
- [Backstage architecture](overview/architecture-overview.md)
|
||||
- [Architecture and terminology](overview/architecture-terminology.md)
|
||||
- [Roadmap](overview/roadmap.md)
|
||||
- Getting started
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,11 +36,7 @@ Spotify’s developer experience offering with 2,400+ documentation sites and
|
||||
|
||||
- [Getting Started]
|
||||
- [Concepts]
|
||||
- [Reading Documentation]
|
||||
- [Writing Documentation]
|
||||
- [Publishing Documentation]
|
||||
- [Contributing]
|
||||
- [Debugging]
|
||||
- [Creating and Publishing Documentation]
|
||||
- [FAQ]
|
||||
|
||||
## Tech Stack
|
||||
@@ -54,11 +50,7 @@ Spotify’s developer experience offering with 2,400+ documentation sites and
|
||||
|
||||
[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
|
||||
@@ -1,11 +1,21 @@
|
||||
# Publishing Documentation
|
||||
# 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 documentation
|
||||
## 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
|
||||
@@ -32,7 +42,23 @@ And then the `~/hello-docs/docs/index.md` should have the following content:
|
||||
This is a basic example of documentation.
|
||||
```
|
||||
|
||||
## Build 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/
|
||||
@@ -1 +0,0 @@
|
||||
# Debugging
|
||||
@@ -1 +0,0 @@
|
||||
# Reading Documentation
|
||||
@@ -1 +0,0 @@
|
||||
# Writing Documentation
|
||||
@@ -74,8 +74,6 @@ 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
|
||||
|
||||
@@ -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 |
@@ -11,17 +11,17 @@ spec:
|
||||
matchLabels:
|
||||
app: backstage
|
||||
component: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backstage
|
||||
component: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: app
|
||||
image: spotify/backstage:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: app
|
||||
protocol: TCP
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backstage
|
||||
component: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: app
|
||||
image: spotify/backstage:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: app
|
||||
protocol: TCP
|
||||
|
||||
@@ -11,17 +11,17 @@ spec:
|
||||
matchLabels:
|
||||
app: backstage
|
||||
component: backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backstage
|
||||
component: backend
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: spotify/backstage-backend:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 7000
|
||||
name: backend
|
||||
protocol: TCP
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backstage
|
||||
component: backend
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: spotify/backstage-backend:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 7000
|
||||
name: backend
|
||||
protocol: TCP
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
"packages": ["packages/*", "plugins/*"],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.1.1-alpha.14"
|
||||
"version": "0.1.1-alpha.16"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ site_description: 'Main documentation for Backstage features and platform APIs'
|
||||
nav:
|
||||
- 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:
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "yarn workspace example-app start",
|
||||
"bundle": "yarn workspace example-app bundle",
|
||||
"build": "lerna run build",
|
||||
"tsc": "tsc",
|
||||
"clean": "backstage-cli clean && lerna run clean",
|
||||
@@ -17,7 +16,7 @@
|
||||
"lint:all": "lerna run lint --",
|
||||
"lint:type-deps": "node scripts/check-type-dependencies.js",
|
||||
"docgen": "lerna run docgen",
|
||||
"docker-build": "yarn bundle && docker build . -t spotify/backstage",
|
||||
"docker-build": "yarn workspace example-app build && docker build . -t spotify/backstage",
|
||||
"docker-build:all": "yarn tsc && yarn build && yarn docker-build && yarn workspace example-backend build-image",
|
||||
"create-plugin": "backstage-cli create-plugin",
|
||||
"remove-plugin": "backstage-cli remove-plugin",
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/core": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-circleci": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-explore": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-github-actions": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-graphiql": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-register-component": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-rollbar": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-tech-radar": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.14",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/core": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-circleci": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-explore": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-github-actions": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-graphiql": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-register-component": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-rollbar": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-tech-radar": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.16",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
@@ -51,7 +51,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli app:serve",
|
||||
"bundle": "backstage-cli app:build",
|
||||
"build": "backstage-cli app:build",
|
||||
"clean": "backstage-cli clean",
|
||||
"test": "backstage-cli test",
|
||||
"test:e2e": "start-server-and-test start http://localhost:3000 cy:dev",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/backend-common",
|
||||
"description": "Common functionality library for Backstage backends",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -29,19 +29,20 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.6",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"helmet": "^3.22.0",
|
||||
"morgan": "^1.10.0",
|
||||
"stoppable": "^1.1.0",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@types/compression": "^1.7.0",
|
||||
"@types/http-errors": "^1.6.3",
|
||||
"@types/morgan": "^1.9.0",
|
||||
|
||||
@@ -96,4 +96,38 @@ describe('errorHandler', () => {
|
||||
expect((await r.get('/NotFoundError')).status).toBe(404);
|
||||
expect((await r.get('/ConflictError')).status).toBe(409);
|
||||
});
|
||||
|
||||
it('logs all 500 errors', async () => {
|
||||
const app = express();
|
||||
|
||||
const mockLogger = { child: jest.fn(), error: jest.fn() };
|
||||
mockLogger.child.mockImplementation(() => mockLogger as any);
|
||||
|
||||
const thrownError = new Error('some error');
|
||||
|
||||
app.use('/breaks', () => {
|
||||
throw thrownError;
|
||||
});
|
||||
app.use(errorHandler({ logger: mockLogger as any }));
|
||||
|
||||
await request(app).get('/breaks');
|
||||
|
||||
expect(mockLogger.error).toHaveBeenCalledWith(thrownError);
|
||||
});
|
||||
|
||||
it('does not log 400 errors', async () => {
|
||||
const app = express();
|
||||
|
||||
const mockLogger = { child: jest.fn(), error: jest.fn() };
|
||||
mockLogger.child.mockImplementation(() => mockLogger as any);
|
||||
|
||||
app.use('/NotFound', () => {
|
||||
throw new errors.NotFoundError();
|
||||
});
|
||||
app.use(errorHandler({ logger: mockLogger as any }));
|
||||
|
||||
await request(app).get('/NotFound');
|
||||
|
||||
expect(mockLogger.error).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
|
||||
import { ErrorRequestHandler, NextFunction, Request, Response } from 'express';
|
||||
import { Logger } from 'winston';
|
||||
import * as errors from '../errors';
|
||||
import { getRootLogger } from '../logging';
|
||||
|
||||
export type ErrorHandlerOptions = {
|
||||
/**
|
||||
@@ -24,6 +26,13 @@ export type ErrorHandlerOptions = {
|
||||
* If not specified, by default shows stack traces only in development mode.
|
||||
*/
|
||||
showStackTraces?: boolean;
|
||||
|
||||
/**
|
||||
* Logger
|
||||
*
|
||||
* If not specified, by default shows stack traces only in development mode.
|
||||
*/
|
||||
logger?: Logger;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -39,12 +48,17 @@ export type ErrorHandlerOptions = {
|
||||
*
|
||||
* @returns An Express error request handler
|
||||
*/
|
||||
|
||||
export function errorHandler(
|
||||
options: ErrorHandlerOptions = {},
|
||||
): ErrorRequestHandler {
|
||||
const showStackTraces =
|
||||
options.showStackTraces ?? process.env.NODE_ENV === 'development';
|
||||
|
||||
const logger = (options.logger || getRootLogger()).child({
|
||||
type: 'errorHandler',
|
||||
});
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
return (
|
||||
error: Error,
|
||||
@@ -61,6 +75,11 @@ export function errorHandler(
|
||||
|
||||
const status = getStatusCode(error);
|
||||
const message = showStackTraces ? error.stack : error.message;
|
||||
|
||||
if (logger && status >= 500) {
|
||||
logger.error(error);
|
||||
}
|
||||
|
||||
response.status(status).send(message);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
export * from './errorHandler';
|
||||
export * from './notFoundHandler';
|
||||
export * from './requestLoggingHandler';
|
||||
export * from './statusCheckHandler';
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import { statusCheckHandler } from './statusCheckHandler';
|
||||
|
||||
describe('statusCheckHandler', () => {
|
||||
it('gives status 200 when using default', async () => {
|
||||
const app = express();
|
||||
app.use('/healthcheck', await statusCheckHandler());
|
||||
|
||||
const response = await request(app).get('/healthcheck');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.text).toBe(JSON.stringify({ status: 'ok' }));
|
||||
});
|
||||
|
||||
it('gives status 200 when status function returns true', async () => {
|
||||
const app = express();
|
||||
const status = { foo: 'bar' };
|
||||
const statusCheck = () => Promise.resolve(status);
|
||||
app.use('/healthcheck', await statusCheckHandler({ statusCheck }));
|
||||
|
||||
const response = await request(app).get('/healthcheck');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.text).toBe(JSON.stringify(status));
|
||||
});
|
||||
|
||||
it('gives status 500 when status check throws an error', async () => {
|
||||
const app = express();
|
||||
const statusCheck = () => {
|
||||
throw Error('error!');
|
||||
};
|
||||
app.use('/healthcheck', await statusCheckHandler({ statusCheck }));
|
||||
|
||||
const response = await request(app).get('/healthcheck');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NextFunction, Request, Response, RequestHandler } from 'express';
|
||||
|
||||
export type StatusCheck = () => Promise<any>;
|
||||
|
||||
export interface StatusCheckHandlerOptions {
|
||||
/**
|
||||
* Optional status function which returns a message.
|
||||
*/
|
||||
statusCheck?: StatusCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Express middleware for status checks.
|
||||
*
|
||||
* This is commonly used to implement healthcheck and readiness routes.
|
||||
*
|
||||
* @param options An optional configuration object.
|
||||
* @returns An Express error request handler
|
||||
*/
|
||||
export async function statusCheckHandler(
|
||||
options: StatusCheckHandlerOptions = {},
|
||||
): Promise<RequestHandler> {
|
||||
const statusCheck: StatusCheck = options.statusCheck
|
||||
? options.statusCheck
|
||||
: () => Promise.resolve({ status: 'ok' });
|
||||
|
||||
return async (_request: Request, response: Response, next: NextFunction) => {
|
||||
try {
|
||||
const status = await statusCheck();
|
||||
response.status(200).header('').send(status);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import express from 'express';
|
||||
import * as winston from 'winston';
|
||||
import request from 'supertest';
|
||||
import { createStatusCheckRouter } from './createStatusCheckRouter';
|
||||
|
||||
describe('createStatusCheckRouter', () => {
|
||||
const logger = winston.createLogger();
|
||||
|
||||
it('gives status 200 when using default path', async () => {
|
||||
const app = express();
|
||||
app.use('', await createStatusCheckRouter({ logger }));
|
||||
|
||||
const response = await request(app).get('/healthcheck');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.text).toBe(JSON.stringify({ status: 'ok' }));
|
||||
});
|
||||
|
||||
it('gives status 200 when using custom path', async () => {
|
||||
const app = express();
|
||||
app.use('', await createStatusCheckRouter({ logger, path: '/ready' }));
|
||||
|
||||
const response = await request(app).get('/ready');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.text).toBe(JSON.stringify({ status: 'ok' }));
|
||||
});
|
||||
|
||||
it('gives status 500 when status check throws an error', async () => {
|
||||
const app = express();
|
||||
const statusCheck = () => {
|
||||
throw Error('error!');
|
||||
};
|
||||
app.use('', await createStatusCheckRouter({ logger, statusCheck }));
|
||||
|
||||
const response = await request(app).get('/healthcheck');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Logger } from 'winston';
|
||||
import Router from 'express-promise-router';
|
||||
import express from 'express';
|
||||
import { errorHandler, statusCheckHandler, StatusCheck } from '../middleware';
|
||||
|
||||
export interface StatusCheckRouterOptions {
|
||||
logger: Logger;
|
||||
path?: string;
|
||||
statusCheck?: StatusCheck;
|
||||
}
|
||||
|
||||
export async function createStatusCheckRouter(
|
||||
options: StatusCheckRouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const router = Router();
|
||||
const { path = '/healthcheck', statusCheck } = options;
|
||||
|
||||
router.use(path, await statusCheckHandler({ statusCheck }));
|
||||
router.use(errorHandler());
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -15,4 +15,5 @@
|
||||
*/
|
||||
|
||||
export { createServiceBuilder } from './createServiceBuilder';
|
||||
export { createStatusCheckRouter } from './createStatusCheckRouter';
|
||||
export type { ServiceBuilder } from './types';
|
||||
|
||||
@@ -45,9 +45,9 @@ export class ServiceBuilderImpl implements ServiceBuilder {
|
||||
// reloading
|
||||
private module: NodeModule;
|
||||
|
||||
constructor(module: NodeModule) {
|
||||
constructor(moduleRef: NodeModule) {
|
||||
this.routers = [];
|
||||
this.module = module;
|
||||
this.module = moduleRef;
|
||||
}
|
||||
|
||||
loadConfig(config: ConfigReader): ServiceBuilder {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import cors from 'cors';
|
||||
import { Router } from 'express';
|
||||
import { Router, RequestHandler } from 'express';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
@@ -64,7 +64,7 @@ export type ServiceBuilder = {
|
||||
* @param root The root URL to bind to (e.g. "/api/function1")
|
||||
* @param router An express router
|
||||
*/
|
||||
addRouter(root: string, router: Router): ServiceBuilder;
|
||||
addRouter(root: string, router: Router | RequestHandler): ServiceBuilder;
|
||||
|
||||
/**
|
||||
* Starts the server using the given settings.
|
||||
|
||||
@@ -29,6 +29,7 @@ AUTH_GOOGLE_CLIENT_ID=x AUTH_GOOGLE_CLIENT_SECRET=x \
|
||||
AUTH_GITHUB_CLIENT_ID=x AUTH_GITHUB_CLIENT_SECRET=x \
|
||||
AUTH_OAUTH2_CLIENT_ID=x AUTH_OAUTH2_CLIENT_SECRET=x \
|
||||
AUTH_OAUTH2_AUTH_URL=x AUTH_OAUTH2_TOKEN_URL=x \
|
||||
ROLLBAR_ACCOUNT_TOKEN=x \
|
||||
SENTRY_TOKEN=x \
|
||||
LOG_LEVEL=debug \
|
||||
yarn start
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"private": true,
|
||||
@@ -18,18 +18,19 @@
|
||||
"migrate:create": "knex migrate:make -x ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.14",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.14",
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.13",
|
||||
"@backstage/plugin-auth-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-identity-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.14",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.16",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.16",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-auth-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-identity-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.1-alpha.16",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"dockerode": "^3.2.0",
|
||||
"express": "^4.17.1",
|
||||
@@ -38,7 +39,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@types/dockerode": "^2.5.32",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
import { ConfigReader, AppConfig } from '@backstage/config';
|
||||
import { loadConfig } from '@backstage/config-loader';
|
||||
import knex from 'knex';
|
||||
import healthcheck from './plugins/healthcheck';
|
||||
import auth from './plugins/auth';
|
||||
import catalog from './plugins/catalog';
|
||||
import identity from './plugins/identity';
|
||||
@@ -38,6 +39,7 @@ import scaffolder from './plugins/scaffolder';
|
||||
import sentry from './plugins/sentry';
|
||||
import proxy from './plugins/proxy';
|
||||
import techdocs from './plugins/techdocs';
|
||||
import graphql from './plugins/graphql';
|
||||
import { PluginEnvironment } from './types';
|
||||
|
||||
function makeCreateEnv(loadedConfigs: AppConfig[]) {
|
||||
@@ -62,6 +64,7 @@ async function main() {
|
||||
const configReader = ConfigReader.fromConfigs(configs);
|
||||
const createEnv = makeCreateEnv(configs);
|
||||
|
||||
const healthcheckEnv = useHotMemoize(module, () => createEnv('healthcheck'));
|
||||
const catalogEnv = useHotMemoize(module, () => createEnv('catalog'));
|
||||
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
|
||||
const authEnv = useHotMemoize(module, () => createEnv('auth'));
|
||||
@@ -70,9 +73,11 @@ async function main() {
|
||||
const rollbarEnv = useHotMemoize(module, () => createEnv('rollbar'));
|
||||
const sentryEnv = useHotMemoize(module, () => createEnv('sentry'));
|
||||
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
|
||||
const graphqlEnv = useHotMemoize(module, () => createEnv('graphql'));
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
.loadConfig(configReader)
|
||||
.addRouter('', await healthcheck(healthcheckEnv))
|
||||
.addRouter('/catalog', await catalog(catalogEnv))
|
||||
.addRouter('/rollbar', await rollbar(rollbarEnv))
|
||||
.addRouter('/scaffolder', await scaffolder(scaffolderEnv))
|
||||
@@ -80,7 +85,8 @@ async function main() {
|
||||
.addRouter('/auth', await auth(authEnv))
|
||||
.addRouter('/identity', await identity(identityEnv))
|
||||
.addRouter('/techdocs', await techdocs(techdocsEnv))
|
||||
.addRouter('/proxy', await proxy(proxyEnv));
|
||||
.addRouter('/proxy', await proxy(proxyEnv))
|
||||
.addRouter('/graphql', await graphql(graphqlEnv));
|
||||
|
||||
await service.start().catch(err => {
|
||||
console.log(err);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createRouter } from '@backstage/plugin-graphql-backend';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({ logger }: PluginEnvironment) {
|
||||
return await createRouter({
|
||||
logger,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createStatusCheckRouter } from '@backstage/backend-common';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createRouter({ logger }: PluginEnvironment) {
|
||||
return await createStatusCheckRouter({ logger, path: '/healthcheck' });
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
GithubPreparer,
|
||||
Preparers,
|
||||
GithubPublisher,
|
||||
CreateReactAppTemplater,
|
||||
Templaters,
|
||||
} from '@backstage/plugin-scaffolder-backend';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
@@ -29,8 +30,10 @@ import Docker from 'dockerode';
|
||||
|
||||
export default async function createPlugin({ logger }: PluginEnvironment) {
|
||||
const cookiecutterTemplater = new CookieCutter();
|
||||
const craTemplater = new CreateReactAppTemplater();
|
||||
const templaters = new Templaters();
|
||||
templaters.register('cookiecutter', cookiecutterTemplater);
|
||||
templaters.register('cra', craTemplater);
|
||||
|
||||
const filePreparer = new FilePreparer();
|
||||
const githubPreparer = new GithubPreparer();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/catalog-model",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,7 +20,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"@types/yup": "^0.28.2",
|
||||
"json-schema": "^0.2.5",
|
||||
@@ -29,7 +29,7 @@
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^25.2.2",
|
||||
"@types/lodash": "^4.14.151",
|
||||
|
||||
@@ -50,6 +50,11 @@ declare module '*.webp' {
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.yaml' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.icon.svg' {
|
||||
import { ComponentType } from 'react';
|
||||
import { SvgIconProps } from '@material-ui/core';
|
||||
|
||||
@@ -38,7 +38,7 @@ async function getConfig() {
|
||||
transform: {
|
||||
'\\.esm\\.js$': require.resolve('jest-esm-transformer'),
|
||||
'\\.(js|jsx|ts|tsx)': require.resolve('ts-jest'),
|
||||
'\\.(bmp|gif|jpe|png|frag|xml|svg)': require.resolve(
|
||||
'\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg)': require.resolve(
|
||||
'./jestFileTransform.js',
|
||||
),
|
||||
},
|
||||
@@ -49,7 +49,7 @@ async function getConfig() {
|
||||
// Default behaviour is to not apply transforms for node_modules, but we still want
|
||||
// to apply the esm-transformer to .esm.js files, since that's what we use in backstage packages.
|
||||
transformIgnorePatterns: [
|
||||
'/node_modules/(?!.*\\.(?:esm\\.js|bmp|gif|jpe|png|frag|xml|svg)$)',
|
||||
'/node_modules/(?!.*\\.(?:esm\\.js|bmp|gif|jpg|jpeg|png|frag|xml|svg)$)',
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -29,14 +29,15 @@
|
||||
"backstage-cli": "bin/backstage-cli"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.13",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.16",
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@lerna/package-graph": "^3.18.5",
|
||||
"@lerna/project": "^3.18.0",
|
||||
"@rollup/plugin-commonjs": "^13.0.0",
|
||||
"@rollup/plugin-json": "^4.0.2",
|
||||
"@rollup/plugin-node-resolve": "^8.1.0",
|
||||
"@rollup/plugin-yaml": "^2.1.1",
|
||||
"@spotify/eslint-config": "^7.0.1",
|
||||
"@sucrase/webpack-loader": "^2.0.0",
|
||||
"@svgr/plugin-jsx": "4.3.x",
|
||||
|
||||
@@ -26,6 +26,7 @@ import imageFiles from 'rollup-plugin-image-files';
|
||||
import svgr from '@svgr/rollup';
|
||||
import dts from 'rollup-plugin-dts';
|
||||
import json from '@rollup/plugin-json';
|
||||
import yaml from '@rollup/plugin-yaml';
|
||||
import { RollupOptions, OutputOptions } from 'rollup';
|
||||
|
||||
import { BuildOptions, Output } from './types';
|
||||
@@ -93,6 +94,7 @@ export const makeConfigs = async (
|
||||
postcss(),
|
||||
imageFiles({ exclude: '**/*.icon.svg' }),
|
||||
json(),
|
||||
yaml(),
|
||||
svgr({
|
||||
include: '**/*.icon.svg',
|
||||
template: svgrTemplate,
|
||||
|
||||
@@ -25,11 +25,6 @@ import { Config } from '@backstage/config';
|
||||
import { BundlingPaths } from './paths';
|
||||
import { transforms } from './transforms';
|
||||
import { BundlingOptions, BackendBundlingOptions } from './types';
|
||||
// import checkRequiredFiles from 'react-dev-utils/checkRequiredFiles';
|
||||
// import ModuleNotFoundPlugin from 'react-dev-utils/ModuleNotFoundPlugin';
|
||||
// import errorOverlayMiddleware from 'react-dev-utils/errorOverlayMiddleware';
|
||||
// import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware';
|
||||
// import WatchMissingNodeModulesPlugin from 'react-dev-utils/WatchMissingNodeModulesPlugin';
|
||||
|
||||
export function resolveBaseUrl(config: Config): URL {
|
||||
const baseUrl = config.getString('app.baseUrl');
|
||||
@@ -126,10 +121,10 @@ export function createConfig(
|
||||
output: {
|
||||
path: paths.targetDist,
|
||||
publicPath: validBaseUrl.pathname,
|
||||
filename: isDev ? '[name].js' : '[name].[hash:8].js',
|
||||
filename: isDev ? '[name].js' : 'static/[name].[hash:8].js',
|
||||
chunkFilename: isDev
|
||||
? '[name].chunk.js'
|
||||
: '[name].[chunkhash:8].chunk.js',
|
||||
: 'static/[name].[chunkhash:8].chunk.js',
|
||||
},
|
||||
plugins,
|
||||
};
|
||||
|
||||
@@ -46,7 +46,7 @@ export const optimization = (
|
||||
},
|
||||
filename: isDev
|
||||
? 'module-[name].js'
|
||||
: 'module-[name].[chunkhash:8].js',
|
||||
: 'static/module-[name].[chunkhash:8].js',
|
||||
priority: 10,
|
||||
minSize: 100000,
|
||||
minChunks: 1,
|
||||
|
||||
@@ -79,7 +79,7 @@ export const transforms = (
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
name: 'static/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -111,8 +111,8 @@ export const transforms = (
|
||||
} else {
|
||||
plugins.push(
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[contenthash:8].css',
|
||||
chunkFilename: '[name].[id].[contenthash:8].css',
|
||||
filename: 'static/[name].[contenthash:8].css',
|
||||
chunkFilename: 'static/[name].[id].[contenthash:8].css',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -134,11 +134,15 @@ async function findTargetPackages(pkgNames: string[]): Promise<LernaPackage[]> {
|
||||
throw new Error(`Package '${name}' not found`);
|
||||
}
|
||||
|
||||
const pkgDeps = Object.keys(node.pkg.dependencies);
|
||||
const localDeps: string[] = Array.from(node.localDependencies.keys());
|
||||
const filteredDeps = localDeps.filter(dep => pkgDeps.includes(dep));
|
||||
// Don't include dependencies of packages that are marked as bundled
|
||||
if (!node.pkg.get('bundled')) {
|
||||
const pkgDeps = Object.keys(node.pkg.dependencies);
|
||||
const localDeps: string[] = Array.from(node.localDependencies.keys());
|
||||
const filteredDeps = localDeps.filter(dep => pkgDeps.includes(dep));
|
||||
|
||||
searchNames.push(...filteredDeps);
|
||||
}
|
||||
|
||||
searchNames.push(...filteredDeps);
|
||||
targets.set(name, node.pkg);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export type Paths = {
|
||||
resolveTargetRoot: ResolveFunc;
|
||||
};
|
||||
|
||||
// Looks for a package.json that has name: "root" to identify the root of the monorepo
|
||||
// Looks for a package.json with a workspace config to identify the root of the monorepo
|
||||
export function findRootPath(topPath: string): string {
|
||||
let path = topPath;
|
||||
|
||||
@@ -58,7 +58,7 @@ export function findRootPath(topPath: string): string {
|
||||
if (exists) {
|
||||
try {
|
||||
const data = fs.readJsonSync(packagePath);
|
||||
if (data.name === 'root' || data.name.includes('backstage-e2e')) {
|
||||
if (data.workspaces?.packages) {
|
||||
return path;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -70,9 +70,7 @@ export function findRootPath(topPath: string): string {
|
||||
|
||||
const newPath = dirname(path);
|
||||
if (newPath === path) {
|
||||
throw new Error(
|
||||
`No package.json with name "root" found as a parent of ${topPath}`,
|
||||
);
|
||||
return topPath; // We didn't find any root package.json, assume we're not in a monorepo
|
||||
}
|
||||
path = newPath;
|
||||
}
|
||||
|
||||
@@ -27,3 +27,5 @@ declare module 'rollup-plugin-image-files' {
|
||||
declare module '@svgr/rollup' {
|
||||
export default function svgr(options?: any): any;
|
||||
}
|
||||
|
||||
declare module '@rollup/plugin-yaml';
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "yarn workspace app start",
|
||||
"bundle": "yarn workspace app bundle",
|
||||
"build": "lerna run build",
|
||||
"tsc": "tsc",
|
||||
"clean": "backstage-cli clean && lerna run clean",
|
||||
@@ -32,7 +31,7 @@
|
||||
"prettier": "^1.19.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/esbuild": "0.5.3"
|
||||
"esbuild": "0.6.3"
|
||||
},
|
||||
"prettier": "@spotify/prettier-config",
|
||||
"lint-staged": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli app:serve",
|
||||
"bundle": "backstage-cli app:build",
|
||||
"build": "backstage-cli app:build",
|
||||
"test": "backstage-cli test",
|
||||
"lint": "backstage-cli lint",
|
||||
"test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/config-loader",
|
||||
"description": "Config loading functionality used by Backstage backend, and CLI",
|
||||
"version": "0.1.1-alpha.13",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,7 +30,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"fs-extra": "^9.0.0",
|
||||
"yaml": "^1.9.2",
|
||||
"yup": "^0.29.1"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/config",
|
||||
"description": "Config API used by Backstage core, backend, and CLI",
|
||||
"version": "0.1.1-alpha.13",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-api",
|
||||
"description": "Internal Core API used by Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,8 +29,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@types/react": "^16.9",
|
||||
@@ -41,8 +41,8 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.13",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.16",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core",
|
||||
"description": "Core API used by Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,9 +29,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/core-api": "^0.1.1-alpha.14",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"@backstage/core-api": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -54,8 +54,8 @@
|
||||
"react-use": "^14.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.16",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -89,7 +89,7 @@ export const WithLink = () => (
|
||||
message={
|
||||
<Typography>
|
||||
This is a dismissable banner with a link:{' '}
|
||||
<Link href="http://example.com" color="textSecondary">
|
||||
<Link href="http://example.com" color="textPrimary">
|
||||
example.com
|
||||
</Link>
|
||||
</Typography>
|
||||
|
||||
@@ -18,13 +18,14 @@ import React, { FC, ReactNode, useState, useEffect } from 'react';
|
||||
import { useApi, storageApiRef } from '@backstage/core-api';
|
||||
import { useObservable } from 'react-use';
|
||||
import classNames from 'classnames';
|
||||
import { makeStyles, Theme } from '@material-ui/core';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import SnackbarContent from '@material-ui/core/SnackbarContent';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Close from '@material-ui/icons/Close';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
const useStyles = makeStyles((theme: BackstageTheme) => ({
|
||||
root: {
|
||||
position: 'relative',
|
||||
padding: theme.spacing(0),
|
||||
@@ -46,10 +47,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||
alignItems: 'center',
|
||||
},
|
||||
info: {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.banner.info,
|
||||
},
|
||||
error: {
|
||||
backgroundColor: theme.palette.error.dark,
|
||||
backgroundColor: theme.palette.banner.error,
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/dev-utils",
|
||||
"description": "Utilities for developing Backstage plugins.",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,10 +29,10 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/core": "^0.1.1-alpha.14",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/core": "^0.1.1-alpha.16",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "storybook",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"description": "Storybook build for core package",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -14,7 +14,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/theme": "^0.1.1-alpha.14"
|
||||
"@backstage/theme": "^0.1.1-alpha.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^5.3.17",
|
||||
|
||||
@@ -4,45 +4,31 @@ Check out the [TechDocs README](https://github.com/spotify/backstage/blob/master
|
||||
|
||||
**WIP: This cli is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/spotify/backstage/milestone/15).**
|
||||
|
||||
## Commands
|
||||
## Prerequisities
|
||||
|
||||
Run the following command from the project root:
|
||||
|
||||
```bash
|
||||
# Serve localhost:3000 (and localhost:8000)
|
||||
yarn serve
|
||||
|
||||
# Serve localhost:8000 containing your Mkdocs documentation.
|
||||
yarn serve:mkdocs
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
## Run TechDocs CLI
|
||||
|
||||
You'll need Docker installed and running to use this. You will also need to build the container located at `/packages/techdocs-container` under the tag `mkdocs:local-dev`, as you can see in the commands from below:
|
||||
|
||||
```bash
|
||||
docker build packages/techdocs-container -t mkdocs:local-dev
|
||||
```
|
||||
|
||||
From that point, you can invoke the CLI from any project with a docs folder. Try out our example!
|
||||
You'll need Docker installed and running to use this.
|
||||
|
||||
```bash
|
||||
cd packages/techdocs-container/mock-docs
|
||||
npx @techdocs/cli serve
|
||||
|
||||
# To get a view of your docs in Backstage, use:
|
||||
npx techdocs-cli serve
|
||||
|
||||
# To view the raw mkdocs site (without Backstage), use:
|
||||
npx techdocs-cli serve:mkdocs
|
||||
```
|
||||
|
||||
## Local Development
|
||||
If you run `npx techdocs-cli serve` you should have a `localhost:3000` serving TechDocs in Backstage, as well as `localhost:8000` serving Mkdocs (which won't open up and be exposed to the user).
|
||||
|
||||
You'll need Docker installed and running to use this. You will also need to build the container located at `packages/techdocs-container` under the tag `mkdocs:local-dev` (for now until we deploy the container to a centralized Docker registry), as you can see in the commands from below:
|
||||
|
||||
```bash
|
||||
docker build packages/techdocs-container -t mkdocs:local-dev
|
||||
```
|
||||
|
||||
```bash
|
||||
cd packages/techdocs-container/mock-docs
|
||||
npx techdocs serve
|
||||
```
|
||||
|
||||
You should have a `localhost:3000` serving TechDocs in Backstage, as well as `localhost:8000` serving Mkdocs (which won't open up and be exposed to the user).
|
||||
If running `npx techdocs-cli serve:mkdocs` you will have `localhost:8000` exposed, serving Mkdocs.
|
||||
|
||||
Happy hacking!
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@techdocs/cli",
|
||||
"description": "CLI for running TechDocs locally.",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -25,7 +25,7 @@
|
||||
"start": "nodemon --"
|
||||
},
|
||||
"bin": {
|
||||
"techdocs": "bin/techdocs-cli"
|
||||
"techdocs-cli": "bin/techdocs-cli"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@spotify/eslint-config": "^7.0.0",
|
||||
@@ -44,7 +44,7 @@
|
||||
"ext": "ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"commander": "^5.1.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"http-proxy": "^1.18.1",
|
||||
|
||||
@@ -68,7 +68,7 @@ const runMkdocsServer = (options?: {
|
||||
'$(pwd):/content',
|
||||
'-p',
|
||||
'8000:8000',
|
||||
'mkdocs:local-dev',
|
||||
'spotify/techdocs',
|
||||
'serve',
|
||||
'-a',
|
||||
devAddr,
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
FROM python:3.7.7-alpine3.12
|
||||
|
||||
RUN apk update && apk --no-cache add gcc musl-dev
|
||||
RUN pip install --upgrade pip && pip install mkdocs==1.1.2 mkdocs-material==5.3.2 mkdocs-monorepo-plugin==0.4.5 pymdown-extensions==7.1
|
||||
|
||||
ADD ./techdocs-core /techdocs-core
|
||||
RUN pip install --no-index /techdocs-core
|
||||
RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==0.0.2 mkdocs==1.1.2 mkdocs-material==5.3.2 mkdocs-monorepo-plugin==0.4.5 pymdown-extensions==7.1
|
||||
|
||||
ENTRYPOINT [ "mkdocs" ]
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ This is the Docker container that powers the creation of static documentation si
|
||||
Using the TechDocs CLI, we can invoke the latest version of `techdocs-container` via Docker Hub:
|
||||
|
||||
```bash
|
||||
npx @techdocs/cli serve
|
||||
npx techdocs-cli serve
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
This is the base [Mkdocs](https://mkdocs.org) plugin used when using Mkdocs with Spotify's TechDocs. It is written in Python and packages all of our Mkdocs defaults, such as theming, plugins, etc in a single plugin.
|
||||
|
||||
[Python Package](https://pypi.org/project/mkdocs-techdocs-core/)
|
||||
|
||||
## Usage
|
||||
|
||||
**Installation instructions TBD.** We haven't published it to a Python registry yet.
|
||||
```bash
|
||||
$ pip install mkdocs-techdocs-core
|
||||
```
|
||||
|
||||
Once you have installed the `mkdocs-techdocs-core` plugin, you'll need to add it to your `mkdocs.yml`.
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='mkdocs-techdocs-core',
|
||||
version='0.0.1',
|
||||
version='0.0.2',
|
||||
description='A Mkdocs package that contains TechDocs defaults',
|
||||
long_description='',
|
||||
keywords='mkdocs',
|
||||
url='https://github.com/spotify/backstage',
|
||||
author='Spotify',
|
||||
author_email='fossboard@spotify.com',
|
||||
author='TechDocs Core',
|
||||
author_email='pulp-fiction@spotify.com',
|
||||
license='Apache-2.0',
|
||||
python_requires='>=3.7',
|
||||
install_requires=[
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/test-utils-core",
|
||||
"description": "Utilities to test Backstage core",
|
||||
"version": "0.1.1-alpha.13",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/test-utils",
|
||||
"description": "Utilities to test Backstage plugins and apps.",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,10 +29,10 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/core-api": "^0.1.1-alpha.14",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.13",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/core-api": "^0.1.1-alpha.16",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/theme",
|
||||
"description": "material-ui theme for use with Backstage.",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -31,7 +31,7 @@
|
||||
"@material-ui/core": "^4.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14"
|
||||
"@backstage/cli": "^0.1.1-alpha.16"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -41,6 +41,10 @@ export const lightTheme = createTheme({
|
||||
primary: {
|
||||
main: blue[500],
|
||||
},
|
||||
banner: {
|
||||
info: '#2E77D0',
|
||||
error: '#E22134',
|
||||
},
|
||||
border: '#E6E6E6',
|
||||
textContrast: '#000000',
|
||||
textVerySubtle: '#DDD',
|
||||
@@ -90,6 +94,10 @@ export const darkTheme = createTheme({
|
||||
primary: {
|
||||
main: blue[500],
|
||||
},
|
||||
banner: {
|
||||
info: '#2E77D0',
|
||||
error: '#E22134',
|
||||
},
|
||||
border: '#E6E6E6',
|
||||
textContrast: '#FFFFFF',
|
||||
textVerySubtle: '#DDD',
|
||||
|
||||
@@ -58,6 +58,10 @@ type PaletteAdditions = {
|
||||
icon: string;
|
||||
background: string;
|
||||
};
|
||||
banner: {
|
||||
info: string;
|
||||
error: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type BackstagePalette = Palette & PaletteAdditions;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-auth-backend",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,9 +20,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.14",
|
||||
"@backstage/config": "^0.1.1-alpha.13",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.13",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.16",
|
||||
"@backstage/config": "^0.1.1-alpha.16",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.16",
|
||||
"@types/express": "^4.17.6",
|
||||
"body-parser": "^1.19.0",
|
||||
"compression": "^1.7.4",
|
||||
@@ -49,7 +49,7 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@types/body-parser": "^1.19.0",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/jwt-decode": "2.2.1",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
exports.up = function up(knex) {
|
||||
return knex.schema.raw(`DROP VIEW location_update_log_latest;`).raw(`
|
||||
CREATE VIEW location_update_log_latest AS
|
||||
SELECT t1.* FROM location_update_log t1
|
||||
JOIN
|
||||
(
|
||||
SELECT location_id, MAX(created_at) AS MAXDATE
|
||||
FROM location_update_log
|
||||
GROUP BY location_id
|
||||
) t2
|
||||
ON t1.location_id = t2.location_id
|
||||
AND t1.created_at = t2.MAXDATE
|
||||
GROUP BY t1.location_id, t1.id
|
||||
ORDER BY created_at DESC;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = function down(knex) {
|
||||
knex.schema.raw(`DROP VIEW location_update_log_latest;`);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog-backend",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,8 +21,8 @@
|
||||
"mock-data": "./scripts/mock-data.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.14",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.14",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.16",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.16",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
@@ -39,7 +39,7 @@
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@types/lodash": "^4.14.151",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/supertest": "^2.0.8",
|
||||
|
||||
@@ -17,3 +17,11 @@ for URL in \
|
||||
--data-raw "{\"type\": \"github\", \"target\": \"https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/${URL}\"}"
|
||||
echo
|
||||
done
|
||||
|
||||
curl \
|
||||
--location \
|
||||
--request POST 'localhost:7000/catalog/locations' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw "{\"type\": \"github\", \"target\": \"https://github.com/benjdlambert/cookiecutter-golang/blob/master/template.yaml\"}"
|
||||
echo
|
||||
|
||||
|
||||
@@ -88,6 +88,31 @@ describe('CommonDatabase', () => {
|
||||
expect(locations).toEqual([output]);
|
||||
const location = await db.location(locations[0].id);
|
||||
expect(location).toEqual(output);
|
||||
|
||||
// If we add 2 new update log events,
|
||||
// this should not result in location duplication
|
||||
// due to incorrect join in DB
|
||||
await db.addLocationUpdateLogEvent(
|
||||
'dd12620d-0436-422f-93bd-929aa0788123',
|
||||
DatabaseLocationUpdateLogStatus.SUCCESS,
|
||||
);
|
||||
|
||||
// Have a second in-between
|
||||
// To avoid having same timestamp on event
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
await db.addLocationUpdateLogEvent(
|
||||
'dd12620d-0436-422f-93bd-929aa0788123',
|
||||
DatabaseLocationUpdateLogStatus.FAIL,
|
||||
);
|
||||
|
||||
expect(await db.locations()).toEqual([
|
||||
{
|
||||
...output,
|
||||
status: DatabaseLocationUpdateLogStatus.FAIL,
|
||||
timestamp: expect.any(String),
|
||||
},
|
||||
]);
|
||||
|
||||
await db.transaction(tx => db.removeLocation(tx, locations[0].id));
|
||||
|
||||
await expect(db.locations()).resolves.toEqual([]);
|
||||
|
||||
@@ -24,3 +24,4 @@ export type {
|
||||
ReadLocationError,
|
||||
ReadLocationResult,
|
||||
} from './types';
|
||||
export * from './processors';
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as results from './results';
|
||||
|
||||
export { results };
|
||||
export * from './types';
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,11 +21,11 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.14",
|
||||
"@backstage/core": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.14",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.14",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.16",
|
||||
"@backstage/core": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -38,9 +38,9 @@
|
||||
"swr": "^0.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.16",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.16",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/react-hooks": "^3.3.0",
|
||||
@@ -50,7 +50,7 @@
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.19.0",
|
||||
"react-test-renderer": "^16.13.1",
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
"whatwg-fetch": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -42,7 +42,7 @@ export const WelcomeBanner = () => {
|
||||
🎉
|
||||
</span>
|
||||
Welcome to Backstage! Take a look around and check out our{' '}
|
||||
<Link href="/welcome" color="textSecondary">
|
||||
<Link href="/welcome" color="textPrimary">
|
||||
getting started
|
||||
</Link>{' '}
|
||||
page.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-circleci",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,8 +21,8 @@
|
||||
"postpack": "backstage-cli postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.14",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/core": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -36,8 +36,8 @@
|
||||
"react-use": "^14.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.16",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-explore",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,8 +21,8 @@
|
||||
"start": "backstage-cli plugin:serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.14",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/core": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -32,9 +32,9 @@
|
||||
"react-use": "^14.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.16",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.16",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -1,13 +1,70 @@
|
||||
# github-actions
|
||||
# GitHub Actions Plugin
|
||||
|
||||
Welcome to the github-actions plugin!
|
||||
Website: [https://github.com/actions](https://github.com/actions)
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
## Screenshots
|
||||
|
||||
## Getting started
|
||||
TBD
|
||||
|
||||
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/github-actions](http://localhost:3000/github-actions).
|
||||
## Setup
|
||||
### Generic Requirements
|
||||
1. Provide OAuth credentials:
|
||||
1. [Create an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/) with callback URL set to ```https://localhost:3000/auth/github```.
|
||||
2. Take Client ID and Client Secret from the newly created app's settings page and put them into ```AUTH_GITHUB_CLIENT_ID``` and ```AUTH_GITHUB_CLIENT_SECRET``` env variables.
|
||||
2. Annotate your component with a correct GitHub Actions repository and owner:
|
||||
|
||||
The annotation key is ```backstage.io/github-actions-id```.
|
||||
|
||||
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
||||
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
||||
It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory.
|
||||
Example:
|
||||
```
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage
|
||||
description: backstage.io
|
||||
annotations:
|
||||
backstage.io/github-actions-id: 'spotify/backstage'
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: guest
|
||||
```
|
||||
### Standalone app requirements
|
||||
If you didn't clone this repo you have to do some extra work.
|
||||
|
||||
1. Add plugin API to your Backstage instance:
|
||||
|
||||
```bash
|
||||
yarn add @backstage/plugin-github-actions
|
||||
```
|
||||
|
||||
```js
|
||||
// packages/app/src/api.ts
|
||||
import { ApiRegistry } from '@backstage/core';
|
||||
import { GithubActionsClient, githubActionsApiRef } from '@backstage/plugin-github-actions';
|
||||
|
||||
const builder = ApiRegistry.builder();
|
||||
builder.add(githubActionsApiRef, new GithubActionsClient());
|
||||
|
||||
export default builder.build() as ApiHolder;
|
||||
```
|
||||
|
||||
2. Add plugin itself:
|
||||
|
||||
```js
|
||||
// packages/app/src/plugins.ts
|
||||
export { plugin as GithubActions } from '@backstage/plugin-github-actions';
|
||||
```
|
||||
|
||||
3. Run the app with `yarn start` and the backend with `yarn --cwd packages/backend start`, navigate to `/github-actions/`.
|
||||
|
||||
## Features
|
||||
|
||||
- List workflow runs for a project
|
||||
- Dive into one run to see a job steps
|
||||
- Retry runs
|
||||
- Pagination for runs
|
||||
|
||||
## Limitations
|
||||
|
||||
- There is a limit of 100 apps for one OAuth client/token pair
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-github-actions",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -18,26 +18,29 @@
|
||||
"diff": "backstage-cli plugin:diff",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
"clean": "backstage-cli clean",
|
||||
"mock-data": "./scripts/mock-data.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.14",
|
||||
"@backstage/core": "^0.1.1-alpha.14",
|
||||
"@backstage/core-api": "^0.1.1-alpha.14",
|
||||
"@backstage/theme": "^0.1.1-alpha.14",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.16",
|
||||
"@backstage/core": "^0.1.1-alpha.16",
|
||||
"@backstage/core-api": "^0.1.1-alpha.16",
|
||||
"@backstage/theme": "^0.1.1-alpha.16",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.16",
|
||||
"@material-ui/core": "^4.9.1",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"@octokit/types": "^5.0.1",
|
||||
"moment": "^2.27.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"react-use": "^14.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.14",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.14",
|
||||
"@backstage/cli": "^0.1.1-alpha.16",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.16",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
curl \
|
||||
--location \
|
||||
--request POST 'localhost:7000/catalog/locations' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw "{\"type\": \"file\", \"target\": \"$(pwd)/scripts/sample.yaml\"}"
|
||||
echo
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage
|
||||
description: backstage.io
|
||||
annotations:
|
||||
backstage.io/github-actions-id: 'spotify/backstage'
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: guest
|
||||
@@ -72,5 +72,5 @@ export type GithubActionsApi = {
|
||||
owner: string;
|
||||
repo: string;
|
||||
runId: number;
|
||||
}) => void;
|
||||
}) => Promise<any>;
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
} from '@octokit/types';
|
||||
|
||||
export class GithubActionsClient implements GithubActionsApi {
|
||||
reRunWorkflow({
|
||||
async reRunWorkflow({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
@@ -33,8 +33,8 @@ export class GithubActionsClient implements GithubActionsApi {
|
||||
owner: string;
|
||||
repo: string;
|
||||
runId: number;
|
||||
}) {
|
||||
new Octokit({ auth: token }).actions.reRunWorkflow({
|
||||
}): Promise<any> {
|
||||
return new Octokit({ auth: token }).actions.reRunWorkflow({
|
||||
owner,
|
||||
repo,
|
||||
run_id: runId,
|
||||
|
||||