Merge branch 'master' into rugvip/docgen

This commit is contained in:
Patrik Oldsberg
2020-07-27 12:56:42 +02:00
committed by GitHub
178 changed files with 3403 additions and 1093 deletions
+8 -6
View File
@@ -4,9 +4,11 @@
# The last matching pattern takes precedence.
# https://help.github.com/articles/about-codeowners/
* @spotify/backstage-core
/docs/features/techdocs @spotify/techdocs-core
/plugins/techdocs @spotify/techdocs-core
/plugins/techdocs-backend @spotify/techdocs-core
/packages/techdocs-cli @spotify/techdocs-core
/packages/techdocs-container @spotify/techdocs-core
* @spotify/backstage-core
/docs/features/techdocs @spotify/techdocs-core
/plugins/techdocs @spotify/techdocs-core
/plugins/techdocs-backend @spotify/techdocs-core
/packages/techdocs-cli @spotify/techdocs-core
/packages/techdocs-container @spotify/techdocs-core
/.github/workflows/techdocs.yml @spotify/techdocs-core
/.github/workflows/techdocs-pypi.yml @spotify/techdocs-core
-3
View File
@@ -76,8 +76,5 @@ jobs:
- name: verify plugin template
run: yarn lerna -- run diff -- --check
- name: bundle example app
run: yarn bundle
- name: verify storybook
run: yarn workspace storybook build-storybook
+37
View File
@@ -0,0 +1,37 @@
name: Master Build TechDocs PyPI Publish
on:
push:
branches: [master]
paths:
- '.github/workflows/techdocs-pypi.yml'
- 'packages/techdocs-container/**'
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
python-version: [3.7]
steps:
# Publish techdocs-core to PyPI
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@master
with:
python-version: 3.7
- name: Build Python distribution
working-directory: ./packages/techdocs-container/techdocs-core
run: |
pip install wheel
rm -rf dist
python setup.py bdist_wheel sdist --formats gztar
- name: Publish a Python distribution to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.PYPI_API_KEY }}
packages_dir: ./packages/techdocs-container/techdocs-core/dist
+2 -2
View File
@@ -33,12 +33,12 @@ jobs:
push: false
# Lint Python code for techdocs-core package
- name: prepare python environment
- name: Prepare Python environment
run: |
python3 -m pip install --index-url https://pypi.org/simple/ setuptools
python3 -m pip install --upgrade pip
python3 -m pip install --index-url https://pypi.org/simple/ -r $TECHDOCS_CORE_PATH/requirements.txt
- name: lint techdocs-core package
- name: Lint techdocs-core package
run: |
python3 -m black --check $TECHDOCS_CORE_PATH/src
+4 -1
View File
@@ -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
+4 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1
View File
@@ -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
+29 -1
View File
@@ -1 +1,29 @@
# FAQ
# TechDocs FAQ
This page answer frequently asked questions about [TechDocs].
#### Technology
- [What static site generator is TechDocs using?](./#what-static-site-generator-is-techdocs-using)
- [What is the mkdocs-techdocs-core plugin?](./#what-is-the-mkdocs-techdocs-core-plugin)
## What static site generator is TechDocs using?
TechDocs is using [MkDocs](https://www.mkdocs.org/) to build project
doucmentation under the hood. Documentation built with the
[techdocs-container](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/README.md)
is using the MkDocs Material Theme.
## What is the mkdocs-techdocs-core plugin?
The
[mkdocs-techdocs-core](https://github.com/spotify/backstage/blob/master/packages/techdocs-container/techdocs-core/README.md)
package is a MkDocs Plugin that works like a wrapper around multiple MkDocs
plugins (e.g.
[MkDocs Monorepo Plugin](https://github.com/spotify/mkdocs-monorepo-plugin)) as
well as a selection of Python Markdown extensions that TechDocs supports.
_Add a question that you think others might be interested in? Edit the file
[here](https://github.com/spotify/backstage/edit/master/docs/features/techdocs/FAQ.md)._
[techdocs]: README.md
+2 -10
View File
@@ -36,11 +36,7 @@ Spotifys 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 @@ Spotifys 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
+52
View File
@@ -1 +1,53 @@
# Concepts
This page describes concepts that has been introduced with Spotify's
docs-like-code solution in Backstage.
### TechDocs Core Plugin
The TechDocs Core Plugin is a MkDocs plugin created as a wrapper around multiple
MkDocs plugins and Python Markdown extensions to standardize the configuration
of MkDocs used for TechDocs.
[TechDocs Core](../../../packages/techdocs-container/techdocs-core/README.md)
### TechDocs container
The TechDocs container is a Docker container available at
[DockerHub](https://hub.docker.com/r/spotify/techdocs). It builds static HTML
pages, including stylesheets and scripts from Python flavored Markdown, through
MkDocs.
[TechDocs Container](../../../packages/techdocs-container/README.md)
### TechDocs publisher (Coming Soon)
### TechDocs CLI
The TechDocs CLI was created to make it easy to write, generate and preview
documentation for publishing. Currently it mostly acts as a wrapper around the
TechDocs container and provides a easy to use interface for our docker
container.
[TechDocs CLI](../../../packages/techdocs-cli/README.md)
### TechDocs Reader
Documentation generated by TechDocs is generated as static html sites. The
TechDocs Reader was therefore created to be able to integrate pre-generated html
sites with the Backstage UI.
The TechDocs Reader purpose is also to open up the opportunity to integrate
TechDocs widgets for a customized full-featured TechDocs experience.
([Coming Soon V.2](https://github.com/spotify/backstage/milestone/17))
[TechDocs Reader](../../../plugins/techdocs/src/reader/README.md)
### Transformers
Transformers is different pieces of functionality used inside the TechDocs
Reader. The reason to why transformers were introduced is to provide a way to
transform the html content on pre and post render. (e.g. rewrite docs links or
modify css)
[Transformers API docs](../../../plugins/techdocs/src/reader/transformers/README.md)
-1
View File
@@ -1 +0,0 @@
# Contributing
@@ -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
View File
@@ -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
+190
View File
@@ -0,0 +1,190 @@
# Typical Backstage architecture
The following diagram shows how Backstage might look when deployed inside a
company which uses the Tech Radar plugin, the Lighthouse plugin, the Circle CI
plugin and the service catalog.
There are 3 main components in this architecture:
1. The core Backstage UI
2. The UI plugins and their backing services
3. Databases
Running this architecture in a real environment typically involves
containerising the components. Various commands are provided for accomplishing
this.
![The architecture of a basic Backstage application](./architecture-overview/backstage-typical-architecture.png)
# The UI
The UI is a thin, client-side wrapper around a set of plugins. It provides some
core UI components and libraries for shared activities such as config
management. [[live demo](https://backstage-demo.roadie.io/)]
![UI with different components highlighted](./architecture-overview/core-vs-plugin-components-highlighted.png)
Each plugin typically makes itself available in the UI on a dedicated URL. For
example, the lighthouse plugin is registered with the UI on `/lighthouse`.
[[live demo](https://backstage-demo.roadie.io/lighthouse)]
![The lighthouse plugin UI](./architecture-overview/lighthouse-plugin.png)
The Circle CI plugin is available on `/circleci`.
![Circle CI Plugin UI](./architecture-overview/circle-ci.png)
# Plugins and plugin backends
Each plugin is a client side application which mounts itself on the UI. Plugins
are written in TypeScript or JavaScript. They each live in their own directory
in `backstage/plugins`. For example, the source code for the lighthouse plugin
is available at
[backstage/plugins/lighthouse](https://github.com/spotify/backstage/tree/master/plugins/lighthouse).
## Installing plugins
Plugins are typically loaded by the UI in your Backstage applications
`plugins.ts` file. For example,
[here](https://github.com/spotify/backstage/blob/master/packages/app/src/plugins.ts)
is that file in the Backstage sample app.
Plugins can be enabled, and passed configuration in `apis.ts`. For example,
[here](https://github.com/spotify/backstage/blob/master/packages/app/src/apis.ts)
is that file in the Backstage sample app.
This is how the lighthouse plugin would be enabled in a typical Backstage
application:
```tsx
import { ApiHolder, ApiRegistry } from '@backstage/core';
import {
lighthouseApiRef,
LighthouseRestApi,
} from '@backstage/plugin-lighthouse';
const builder = ApiRegistry.builder();
export const lighthouseApi = new LighthouseRestApi(/* URL of the lighthouse microservice! */);
builder.add(lighthouseApiRef, lighthouseApi);
export default builder.build() as ApiHolder;
```
As of this moment, there is no config based install procedure for plugins. Some
code changes are required.
## Plugin architecture
Architecturally, plugins can take three forms:
1. Standalone
2. Service backed
3. Third-party backed
### Standalone plugins
Standalone plugins run entirely in the browser.
[The tech radar plugin](https://backstage-demo.roadie.io/tech-radar), for
example, simply renders hard-coded information. It doesn't make any API requests
to other services.
![tech radar plugin ui](./architecture-overview/tech-radar-plugin.png)
The architecture of the Tech Radar installed into a Backstage app is very
simple.
![ui and tech radar plugin connected together](./architecture-overview/tech-radar-plugin-architecture.png)
### Service backed plugins
Service backed plugins make API requests to a service which is within the
purview of the organisation running Backstage.
The lighthouse plugin, for example, makes requests to the
[lighthouse-audit-service](https://github.com/spotify/lighthouse-audit-service).
The lighthouse-audit-service is a microservice which runs a copy of Google's
[Lighthouse library](https://github.com/GoogleChrome/lighthouse/) and stores the
results in a PostgreSQL database.
Its architecture looks like this:
![lighthouse plugin backed to microservice and database](./architecture-overview/lighthouse-plugin-architecture.png)
The service catalog in Backstage is another example of a service backed plugin.
It retrieves a list of services, or "entities", from the Backstage Backend
service and renders them in a table for the user.
### Third-party backed plugins
Third-party backed plugins are similar to service backed plugins. The main
difference is that the service which backs the plugin is hosted outside of the
ecosystem of the company hosting Backstage.
The Circle CI plugin is an example of a third-party backed plugin. Circle CI is
a SaaS service which can be used without any knowledge of Backstage. It has an
API which a Backstage plugin consumes to display content.
Requests which go to Circle CI from the users browser are passed through a proxy
service that Backstage provides. Without this, the requests would be blocked by
Cross Origin Resource Sharing policies which prevent a browser page served at
[https://example.com](https://example.com) from serving resources hosted at
https://circleci.com.
![CircleCi plugin talking to proxy talking to SaaS Circle CI](./architecture-overview/circle-ci-plugin-architecture.png)
# Databases
As we have seen, both the lighthouse-audit-service and catalog-backend require a
database to work with.
At the time of writing, the lighthouse-audit-service requires PostgreSQL to work
with. The service catalog backend uses an in-memory Sqlite3 instance. This is a
development oriented setup and there are plans to support other databases in the
future.
To learn more about the future of databases and Backstage, see the following two
GitHub issues.
[Knex + Plugins (Multiple vs Single Database) · Issue #1598 · spotify/backstage](https://github.com/spotify/backstage/issues/1598)
[Update migrations to support postgres by dariddler · Pull Request #1527 · spotify/backstage](https://github.com/spotify/backstage/pull/1527#discussion_r450374145)
# Containerization
The example Backstage architecture shown above would Dockerize into three
separate docker images.
1. The frontend container
2. The backend container
3. The lighthouse audit service container
![Boxes around the architecture to indicate how it is containerised](./architecture-overview/containerised.png)
The frontend container can be built with a provided command.
```bash
yarn install
yarn tsc
yarn build
yarn run docker-build
```
Running this will simply generate a Docker container containing the contents of
the UIs `dist` directory. The resulting container will be about 50MB in size.
The backend container can be built by running the following command in the
`packages/backend` directory.
```bash
yarn run build-image
```
This will create a ~500MB container called `example-backend`.
The lighthouse-audit-service container is already publicly available in Docker
Hub and can be downloaded and ran with
```bash
docker run spotify/lighthouse-audit-service:latest
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

+14 -14
View File
@@ -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
+14 -14
View File
@@ -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
+1 -1
View File
@@ -2,5 +2,5 @@
"packages": ["packages/*", "plugins/*"],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.1.1-alpha.14"
"version": "0.1.1-alpha.16"
}
+1
View File
@@ -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:
+1 -2
View File
@@ -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",
+20 -20
View File
@@ -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",
+4 -3
View File
@@ -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 {
+2 -2
View File
@@ -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.
+1
View File
@@ -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
+15 -14
View File
@@ -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",
+7 -1
View File
@@ -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);
+39
View File
@@ -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();
+3 -3
View File
@@ -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",
+5
View File
@@ -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';
+2 -2
View File
@@ -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)$)',
],
};
+4 -3
View File
@@ -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",
+2
View File
@@ -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,
+2 -7
View File
@@ -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,
};
+1 -1
View File
@@ -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,
+3 -3
View File
@@ -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',
}),
);
}
+8 -4
View File
@@ -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);
}
+3 -5
View File
@@ -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;
}
+2
View File
@@ -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",
+2 -2
View File
@@ -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 -1
View File
@@ -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",
+5 -5
View File
@@ -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",
+6 -6
View File
@@ -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,
},
}));
+5 -5
View File
@@ -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",
+2 -2
View File
@@ -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",
+14 -28
View File
@@ -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!
+3 -3
View File
@@ -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",
+1 -1
View File
@@ -68,7 +68,7 @@ const runMkdocsServer = (options?: {
'$(pwd):/content',
'-p',
'8000:8000',
'mkdocs:local-dev',
'spotify/techdocs',
'serve',
'-a',
devAddr,
+1 -5
View File
@@ -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" ]
+1 -1
View File
@@ -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 -1
View File
@@ -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",
+5 -5
View File
@@ -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",
+2 -2
View File
@@ -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"
+8
View File
@@ -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',
+4
View File
@@ -58,6 +58,10 @@ type PaletteAdditions = {
icon: string;
background: string;
};
banner: {
info: string;
error: string;
};
};
export type BackstagePalette = Palette & PaletteAdditions;
+5 -5
View File
@@ -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;`);
};
+4 -4
View File
@@ -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';
+10 -10
View File
@@ -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.
+5 -5
View File
@@ -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",
+6 -6
View File
@@ -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",
+65 -8
View File
@@ -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
+11 -8
View File
@@ -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",
+8
View File
@@ -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,

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