Merge branch 'master' into timurista
@@ -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,8 +1,13 @@
|
||||
| Organization | Contact | Description of Use |
|
||||
| ---------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
||||
| [Spotify](https://www.spotify.com) | [@alund](https://github.com/alund) | Main interface towards all of Spotify's infrastructure and technical documentation. |
|
||||
| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. |
|
||||
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
|
||||
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
|
||||
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
|
||||
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
|
||||
| Organization | Contact | Description of Use |
|
||||
| ---------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------- |
|
||||
| [Spotify](https://www.spotify.com) | [@stefanalund](https://github.com/stefanalund) | Main interface towards all of Spotify's infrastructure and technical documentation. |
|
||||
| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. |
|
||||
| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. |
|
||||
| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up |
|
||||
| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. |
|
||||
| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. |
|
||||
| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. |
|
||||
| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications |
|
||||
| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. |
|
||||
| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. |
|
||||
| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D
|
||||
|
||||
@@ -11,14 +11,17 @@ backend:
|
||||
credentials: true
|
||||
|
||||
proxy:
|
||||
'/circleci/api':
|
||||
target: 'https://circleci.com/api/v1.1'
|
||||
"/circleci/api":
|
||||
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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
We have a [Figma component library](https://www.figma.com/@backstage) that you can use to build your own plugins for Backstage.
|
||||
|
||||
@@ -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 |
@@ -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",
|
||||
@@ -16,7 +15,7 @@
|
||||
"lint": "lerna run lint --since origin/master --",
|
||||
"lint:all": "lerna run lint --",
|
||||
"lint:type-deps": "node scripts/check-type-dependencies.js",
|
||||
"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,28 +1,29 @@
|
||||
{
|
||||
"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",
|
||||
"history": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.12.0",
|
||||
@@ -50,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",
|
||||
|
||||
@@ -82,7 +82,9 @@ export const apis = (config: ConfigApi) => {
|
||||
circleCIApiRef,
|
||||
new CircleCIApi(`${backendUrl}/proxy/circleci/api`),
|
||||
);
|
||||
|
||||
builder.add(githubActionsApiRef, new GithubActionsClient());
|
||||
|
||||
builder.add(featureFlagsApiRef, new FeatureFlags());
|
||||
|
||||
builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003'));
|
||||
|
||||
@@ -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,7 +29,7 @@
|
||||
"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",
|
||||
@@ -41,7 +41,7 @@
|
||||
"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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -38,6 +38,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[]) {
|
||||
@@ -70,6 +71,7 @@ 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)
|
||||
@@ -80,7 +82,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,
|
||||
});
|
||||
}
|
||||
@@ -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",
|
||||
@@ -53,6 +54,7 @@
|
||||
"css-loader": "^3.5.3",
|
||||
"dashify": "^2.0.0",
|
||||
"diff": "^4.0.2",
|
||||
"esbuild": "0.6.3",
|
||||
"eslint": "^7.1.0",
|
||||
"eslint-formatter-friendly": "^7.0.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
|
||||
@@ -19,8 +19,12 @@ import { createDistWorkspace } from '../../lib/packager';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { run } from '../../lib/run';
|
||||
|
||||
const PKG_PATH = 'package.json';
|
||||
|
||||
export default async (imageTag: string) => {
|
||||
const tempDistWorkspace = await createDistWorkspace(['example-backend'], {
|
||||
const pkgPath = paths.resolveTarget(PKG_PATH);
|
||||
const pkg = await fs.readJson(pkgPath);
|
||||
const tempDistWorkspace = await createDistWorkspace([pkg.name], {
|
||||
files: [
|
||||
'package.json',
|
||||
'yarn.lock',
|
||||
@@ -28,7 +32,6 @@ export default async (imageTag: string) => {
|
||||
{ src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' },
|
||||
],
|
||||
});
|
||||
|
||||
console.log(`Dist workspace ready at ${tempDistWorkspace}`);
|
||||
|
||||
await run('docker', ['build', '.', '-t', imageTag], {
|
||||
|
||||
@@ -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',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export type PageTheme = {
|
||||
|
||||
export const gradients: Record<string, Gradient> = {
|
||||
darkGrey: {
|
||||
colors: ['#181818', '#404040'],
|
||||
colors: ['#171717', '#383838'],
|
||||
waveColor: '#757575',
|
||||
opacity: ['1.0', '0.0'],
|
||||
},
|
||||
|
||||
@@ -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,44 +4,45 @@ 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!
|
||||
|
||||
## Deploying a new version
|
||||
|
||||
Deploying the Node packages to NPM happens automatically on merge to `master` through GitHub Actions. The deployment happens through Lerna which determines which packages throughout the Backstage project have changed. In our case, the package is called `techdocs-cli` in the repository but `@techdocs/cli` in the NPM registry.
|
||||
|
||||
> Note: Once a package is published under a version, any subsequent changes will not override that version. You will need to bump up the version across the entire Backstage repository, which can be done through Lerna (see the command below).
|
||||
|
||||
In order to bump up all packages, go to the root of the Backstage repository. To see the current version see the `lerna.json` under the `version` key. To then update all the versions (locally on your machine), run the following:
|
||||
|
||||
```bash
|
||||
git checkout -b bump-up-version
|
||||
yarn lerna version --no-push --allow-branch --yes
|
||||
```
|
||||
|
||||
Upon being merged to master, Lerna will then automatically publish these packages as configured by the Backstage core team.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('OAuthProvider Utils', () => {
|
||||
} as unknown) as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrowError('Missing nonce');
|
||||
}).toThrowError('Auth response is missing cookie nonce');
|
||||
});
|
||||
|
||||
it('should throw error if state nonce missing', () => {
|
||||
@@ -63,7 +63,7 @@ describe('OAuthProvider Utils', () => {
|
||||
} as unknown) as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrowError('Missing nonce');
|
||||
}).toThrowError('Auth response is missing state nonce');
|
||||
});
|
||||
|
||||
it('should throw error if nonce mismatch', () => {
|
||||
|
||||
@@ -43,10 +43,12 @@ export const verifyNonce = (req: express.Request, providerId: string) => {
|
||||
const cookieNonce = req.cookies[`${providerId}-nonce`];
|
||||
const stateNonce = req.query.state;
|
||||
|
||||
if (!cookieNonce || !stateNonce) {
|
||||
throw new Error('Missing nonce');
|
||||
if (!cookieNonce) {
|
||||
throw new Error('Auth response is missing cookie nonce');
|
||||
}
|
||||
if (!stateNonce) {
|
||||
throw new Error('Auth response is missing state nonce');
|
||||
}
|
||||
|
||||
if (cookieNonce !== stateNonce) {
|
||||
throw new Error('Invalid nonce');
|
||||
}
|
||||
@@ -151,9 +153,8 @@ export class OAuthProvider implements AuthProviderRouteHandlers {
|
||||
}
|
||||
|
||||
if (!this.options.disableRefresh) {
|
||||
// throw error if missing refresh token
|
||||
if (!refreshToken) {
|
||||
throw new Error('Missing refresh token');
|
||||
throw new InputError('Missing refresh token');
|
||||
}
|
||||
|
||||
// set new refresh token
|
||||
|
||||
@@ -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,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",
|
||||
@@ -21,21 +21,23 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"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",
|
||||
"@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",
|
||||
"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",
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
import { createApiRef } from '@backstage/core';
|
||||
import { Build, BuildDetails } from './types';
|
||||
import {
|
||||
ActionsListWorkflowRunsForRepoResponseData,
|
||||
ActionsGetWorkflowResponseData,
|
||||
ActionsGetWorkflowRunResponseData,
|
||||
} from '@octokit/types';
|
||||
|
||||
export const githubActionsApiRef = createApiRef<GithubActionsApi>({
|
||||
id: 'plugin.githubactions.service',
|
||||
@@ -23,14 +27,50 @@ export const githubActionsApiRef = createApiRef<GithubActionsApi>({
|
||||
});
|
||||
|
||||
export type GithubActionsApi = {
|
||||
listBuilds: ({
|
||||
listWorkflowRuns: ({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
token,
|
||||
pageSize,
|
||||
page,
|
||||
}: {
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
pageSize?: number;
|
||||
page?: number;
|
||||
}) => Promise<ActionsListWorkflowRunsForRepoResponseData>;
|
||||
getWorkflow: ({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
id,
|
||||
}: {
|
||||
token: string;
|
||||
}) => Promise<Build[]>;
|
||||
getBuild: (buildUri: string, token: Promise<string>) => Promise<BuildDetails>;
|
||||
owner: string;
|
||||
repo: string;
|
||||
id: number;
|
||||
}) => Promise<ActionsGetWorkflowResponseData>;
|
||||
getWorkflowRun: ({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
id,
|
||||
}: {
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
id: number;
|
||||
}) => Promise<ActionsGetWorkflowRunResponseData>;
|
||||
reRunWorkflow: ({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
runId,
|
||||
}: {
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
runId: number;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
@@ -15,117 +15,88 @@
|
||||
*/
|
||||
|
||||
import { GithubActionsApi } from './GithubActionsApi';
|
||||
import { Build, BuildDetails, BuildStatus, WorkflowRun } from './types';
|
||||
|
||||
const statusToBuildStatus: { [status: string]: BuildStatus } = {
|
||||
success: BuildStatus.Success,
|
||||
failure: BuildStatus.Failure,
|
||||
pending: BuildStatus.Pending,
|
||||
running: BuildStatus.Running,
|
||||
in_progress: BuildStatus.Running,
|
||||
completed: BuildStatus.Success,
|
||||
};
|
||||
|
||||
const conclusionToStatus = (conslusion: string): BuildStatus =>
|
||||
statusToBuildStatus[conslusion] ?? BuildStatus.Null;
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import {
|
||||
ActionsListWorkflowRunsForRepoResponseData,
|
||||
ActionsGetWorkflowResponseData,
|
||||
ActionsGetWorkflowRunResponseData,
|
||||
} from '@octokit/types';
|
||||
|
||||
export class GithubActionsClient implements GithubActionsApi {
|
||||
async listBuilds({
|
||||
reRunWorkflow({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
token,
|
||||
runId,
|
||||
}: {
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
token: string;
|
||||
}): Promise<Build[]> {
|
||||
const url = `https://api.github.com/repos/${owner}/${repo}/actions/runs`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: new Headers({
|
||||
Authorization: `Bearer ${token}`,
|
||||
}),
|
||||
runId: number;
|
||||
}) {
|
||||
new Octokit({ auth: token }).actions.reRunWorkflow({
|
||||
owner,
|
||||
repo,
|
||||
run_id: runId,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return [
|
||||
{
|
||||
commitId: 'Error',
|
||||
message: 'Response status is not OK',
|
||||
branch: 'Error',
|
||||
status: BuildStatus.Failure,
|
||||
uri: 'Error',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const newData: WorkflowRun[] = data.workflow_runs;
|
||||
|
||||
const endData: Build[] = [];
|
||||
|
||||
newData.forEach((element, index) => {
|
||||
const transData: Build = {
|
||||
commitId: '',
|
||||
message: '',
|
||||
branch: '',
|
||||
status: BuildStatus.Null,
|
||||
uri: '',
|
||||
};
|
||||
transData.commitId = String(element.head_commit.id);
|
||||
transData.branch = element.head_branch;
|
||||
transData.status = conclusionToStatus(element.conclusion);
|
||||
transData.message = element.head_commit.message;
|
||||
transData.uri = element.url;
|
||||
endData[index] = transData;
|
||||
});
|
||||
|
||||
return endData;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async getBuild(
|
||||
buildUri: string,
|
||||
token: Promise<string>,
|
||||
): Promise<BuildDetails> {
|
||||
const response = await fetch(buildUri, {
|
||||
headers: new Headers({
|
||||
Authorization: `Bearer ${await token}`,
|
||||
}),
|
||||
async listWorkflowRuns({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
pageSize = 100,
|
||||
page = 0,
|
||||
}: {
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
pageSize?: number;
|
||||
page?: number;
|
||||
}): Promise<ActionsListWorkflowRunsForRepoResponseData> {
|
||||
const workflowRuns = await new Octokit({
|
||||
auth: token,
|
||||
}).actions.listWorkflowRunsForRepo({
|
||||
owner,
|
||||
repo,
|
||||
per_page: pageSize,
|
||||
page,
|
||||
});
|
||||
const buildBlank: Build = {
|
||||
commitId: '',
|
||||
message: '',
|
||||
branch: '',
|
||||
status: BuildStatus.Null,
|
||||
uri: '',
|
||||
};
|
||||
|
||||
const dataBlank: BuildDetails = {
|
||||
build: buildBlank,
|
||||
author: '',
|
||||
logUrl: '',
|
||||
overviewUrl: '',
|
||||
};
|
||||
|
||||
if (!response.ok) {
|
||||
return dataBlank;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const newData: WorkflowRun = data;
|
||||
|
||||
dataBlank.author = newData.head_commit.author.name;
|
||||
dataBlank.build.branch = newData.head_branch;
|
||||
dataBlank.build.commitId = newData.head_commit.id;
|
||||
dataBlank.build.message = newData.head_commit.message;
|
||||
dataBlank.build.status = conclusionToStatus(newData.status);
|
||||
dataBlank.build.uri = newData.url;
|
||||
dataBlank.logUrl = newData.logs_url;
|
||||
dataBlank.overviewUrl = newData.html_url;
|
||||
|
||||
return dataBlank;
|
||||
return workflowRuns.data;
|
||||
}
|
||||
async getWorkflow({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
id,
|
||||
}: {
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
id: number;
|
||||
}): Promise<ActionsGetWorkflowResponseData> {
|
||||
const workflow = await new Octokit({ auth: token }).actions.getWorkflow({
|
||||
owner,
|
||||
repo,
|
||||
workflow_id: id,
|
||||
});
|
||||
return workflow.data;
|
||||
}
|
||||
async getWorkflowRun({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
id,
|
||||
}: {
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
id: number;
|
||||
}): Promise<ActionsGetWorkflowRunResponseData> {
|
||||
const run = await new Octokit({ auth: token }).actions.getWorkflowRun({
|
||||
owner,
|
||||
repo,
|
||||
run_id: id,
|
||||
});
|
||||
return run.data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
LinearProgress,
|
||||
makeStyles,
|
||||
Paper,
|
||||
@@ -29,9 +28,8 @@ import {
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useAsync } from 'react-use';
|
||||
import { BuildStatusIndicator } from '../BuildStatusIndicator';
|
||||
import { Link, useApi, githubAuthApiRef } from '@backstage/core';
|
||||
import { githubActionsApiRef } from '../../api';
|
||||
|
||||
@@ -49,17 +47,26 @@ const useStyles = makeStyles<Theme>(theme => ({
|
||||
}));
|
||||
|
||||
export const BuildDetailsPage = () => {
|
||||
const repo = 'try-ssr';
|
||||
const owner = 'CircleCITest3';
|
||||
const api = useApi(githubActionsApiRef);
|
||||
const githubApi = useApi(githubAuthApiRef);
|
||||
const token = githubApi.getAccessToken('repo');
|
||||
const auth = useApi(githubAuthApiRef);
|
||||
|
||||
const classes = useStyles();
|
||||
const location = useLocation();
|
||||
const status = useAsync(
|
||||
() =>
|
||||
api.getBuild(decodeURIComponent(location.search.split('uri=')[1]), token),
|
||||
[location.search],
|
||||
);
|
||||
const { id } = useParams();
|
||||
const status = useAsync(async () => {
|
||||
const token = await auth.getAccessToken(['repo', 'user']);
|
||||
return api
|
||||
.getWorkflowRun({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
id: parseInt(id, 10),
|
||||
})
|
||||
.then(data => {
|
||||
return data;
|
||||
});
|
||||
}, [location.search]);
|
||||
|
||||
if (status.loading) {
|
||||
return <LinearProgress />;
|
||||
@@ -90,55 +97,42 @@ export const BuildDetailsPage = () => {
|
||||
<TableCell>
|
||||
<Typography noWrap>Branch</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{details?.build.branch}</TableCell>
|
||||
<TableCell>{details?.head_branch}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Message</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{details?.build.message}</TableCell>
|
||||
<TableCell>{details?.head_commit.message}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Commit ID</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{details?.build.commitId}</TableCell>
|
||||
<TableCell>{details?.head_commit.id}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Status</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<BuildStatusIndicator status={details?.build.status} />
|
||||
</TableCell>
|
||||
<TableCell>{details?.status}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Author</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{details?.author}</TableCell>
|
||||
<TableCell>{`${details?.head_commit.author.name} (${details?.head_commit.author.email})`}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Links</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<ButtonGroup
|
||||
variant="text"
|
||||
color="primary"
|
||||
aria-label="text primary button group"
|
||||
>
|
||||
{details?.overviewUrl && (
|
||||
<Button>
|
||||
<a href={details.overviewUrl}>GitHub</a>
|
||||
</Button>
|
||||
)}
|
||||
{details?.logUrl && (
|
||||
<Button>
|
||||
<a href={details.logUrl}>Logs</a>
|
||||
</Button>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
{details?.html_url && (
|
||||
<Button>
|
||||
<a href={details.html_url}>GitHub</a>
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import React from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { BuildStatusIndicator } from '../BuildStatusIndicator';
|
||||
import { githubActionsApiRef } from '../../api';
|
||||
import { githubActionsApiRef, BuildStatus } from '../../api';
|
||||
import { Link, useApi, githubAuthApiRef } from '@backstage/core';
|
||||
|
||||
const useStyles = makeStyles<Theme>(theme => ({
|
||||
@@ -41,11 +41,11 @@ const useStyles = makeStyles<Theme>(theme => ({
|
||||
|
||||
const BuildInfoCardContent = () => {
|
||||
const api = useApi(githubActionsApiRef);
|
||||
const githubApi = useApi(githubAuthApiRef);
|
||||
const auth = useApi(githubAuthApiRef);
|
||||
|
||||
const status = useAsync(async () => {
|
||||
const token = await githubApi.getAccessToken('repo');
|
||||
return api.listBuilds({ owner: 'spotify', repo: 'backstage', token });
|
||||
const token = await auth.getAccessToken(['repo', 'user']);
|
||||
return api.listWorkflowRuns({ token, owner: 'spotify', repo: 'backstage' });
|
||||
});
|
||||
|
||||
if (status.loading) {
|
||||
@@ -58,8 +58,8 @@ const BuildInfoCardContent = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const [build] =
|
||||
status.value?.filter(({ branch }) => branch === 'master') ?? [];
|
||||
// const [build] =
|
||||
// status.value?.filter(({ branch }) => branch === 'master') ?? [];
|
||||
|
||||
return (
|
||||
<Table>
|
||||
@@ -69,8 +69,8 @@ const BuildInfoCardContent = () => {
|
||||
<Typography noWrap>Message</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Link to={`builds/${encodeURIComponent(build?.uri || '')}`}>
|
||||
<Typography color="primary">{build?.message}</Typography>
|
||||
<Link to="builds/123">
|
||||
<Typography color="primary">build message</Typography>
|
||||
</Link>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -78,14 +78,14 @@ const BuildInfoCardContent = () => {
|
||||
<TableCell>
|
||||
<Typography noWrap>Commit ID</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{build?.commitId}</TableCell>
|
||||
<TableCell>build commit id</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Status</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<BuildStatusIndicator status={build?.status} />
|
||||
<BuildStatusIndicator status={BuildStatus.Success} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
|
||||
@@ -47,7 +47,7 @@ export const BuildListPage = () => {
|
||||
</ContentHeader>
|
||||
<Grid container spacing={3} direction="column">
|
||||
<Grid item>
|
||||
<BuildListTable />
|
||||
<BuildListTable repo="try-ssr" owner="CircleCITest3" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
|
||||
@@ -74,7 +74,7 @@ const generatedColumns: TableColumn[] = [
|
||||
field: 'buildName',
|
||||
highlight: true,
|
||||
render: (row: Partial<Build>) => (
|
||||
<Link component={RouterLink} to={`/build/${row.id}`}>
|
||||
<Link component={RouterLink} to={`/github-actions/build/${row.id}`}>
|
||||
{row.buildName}
|
||||
</Link>
|
||||
),
|
||||
@@ -161,16 +161,23 @@ const BuildListTableView: FC<Props> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export const BuildListTable = () => {
|
||||
const [tableProps] = useBuilds();
|
||||
export const BuildListTable = ({
|
||||
repo,
|
||||
owner,
|
||||
}: {
|
||||
repo: string;
|
||||
owner: string;
|
||||
}) => {
|
||||
const [tableProps, { retry, setPage, setPageSize }] = useBuilds({
|
||||
repo,
|
||||
owner,
|
||||
});
|
||||
return (
|
||||
<BuildListTableView
|
||||
{...tableProps}
|
||||
retry={noop}
|
||||
onChangePageSize={noop}
|
||||
onChangePage={noop}
|
||||
retry={retry}
|
||||
onChangePageSize={setPageSize}
|
||||
onChangePage={setPage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,301 +13,62 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useAsyncRetry } from 'react-use';
|
||||
import { Build } from './BuildListTable';
|
||||
import { githubActionsApiRef } from '../../api/GithubActionsApi';
|
||||
import { useApi, githubAuthApiRef } from '@backstage/core';
|
||||
import { ActionsListWorkflowRunsForRepoResponseData } from '@octokit/types';
|
||||
|
||||
// TODO(shmidt-i): use real APIs
|
||||
const useEntityGHSettings = () => ({ repo: 'test', owner: 'shmidt-i-test' });
|
||||
const buildsMock = {
|
||||
total_count: 1,
|
||||
workflow_runs: [
|
||||
{
|
||||
id: 30433642,
|
||||
node_id: 'MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ==',
|
||||
head_branch: 'master',
|
||||
head_sha: 'acb5820ced9479c074f688cc328bf03f341a511d',
|
||||
run_number: 562,
|
||||
event: 'push',
|
||||
status: 'queued',
|
||||
conclusion: null,
|
||||
workflow_id: 159038,
|
||||
url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642',
|
||||
html_url: 'https://github.com/octo-org/octo-repo/actions/runs/30433642',
|
||||
pull_requests: [],
|
||||
created_at: '2020-01-22T19:33:08Z',
|
||||
updated_at: '2020-01-22T19:33:08Z',
|
||||
jobs_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs',
|
||||
logs_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs',
|
||||
check_suite_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/check-suites/414944374',
|
||||
artifacts_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts',
|
||||
cancel_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel',
|
||||
rerun_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun',
|
||||
workflow_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/actions/workflows/159038',
|
||||
head_commit: {
|
||||
id: 'acb5820ced9479c074f688cc328bf03f341a511d',
|
||||
tree_id: 'd23f6eedb1e1b9610bbc754ddb5197bfe7271223',
|
||||
message: 'Create linter.yml',
|
||||
timestamp: '2020-01-22T19:33:05Z',
|
||||
author: {
|
||||
name: 'Octo Cat',
|
||||
email: 'octocat@github.com',
|
||||
},
|
||||
committer: {
|
||||
name: 'GitHub',
|
||||
email: 'noreply@github.com',
|
||||
},
|
||||
},
|
||||
repository: {
|
||||
id: 1296269,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkxMjk2MjY5',
|
||||
name: 'Hello-World',
|
||||
full_name: 'octocat/Hello-World',
|
||||
owner: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/octocat',
|
||||
html_url: 'https://github.com/octocat',
|
||||
followers_url: 'https://api.github.com/users/octocat/followers',
|
||||
following_url:
|
||||
'https://api.github.com/users/octocat/following{/other_user}',
|
||||
gists_url: 'https://api.github.com/users/octocat/gists{/gist_id}',
|
||||
starred_url:
|
||||
'https://api.github.com/users/octocat/starred{/owner}{/repo}',
|
||||
subscriptions_url:
|
||||
'https://api.github.com/users/octocat/subscriptions',
|
||||
organizations_url: 'https://api.github.com/users/octocat/orgs',
|
||||
repos_url: 'https://api.github.com/users/octocat/repos',
|
||||
events_url: 'https://api.github.com/users/octocat/events{/privacy}',
|
||||
received_events_url:
|
||||
'https://api.github.com/users/octocat/received_events',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
private: false,
|
||||
html_url: 'https://github.com/octocat/Hello-World',
|
||||
description: 'This your first repo!',
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/octocat/Hello-World',
|
||||
archive_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}',
|
||||
assignees_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/assignees{/user}',
|
||||
blobs_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}',
|
||||
branches_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/branches{/branch}',
|
||||
collaborators_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}',
|
||||
comments_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/comments{/number}',
|
||||
commits_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/commits{/sha}',
|
||||
compare_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}',
|
||||
contents_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/contents/{+path}',
|
||||
contributors_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/contributors',
|
||||
deployments_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/deployments',
|
||||
downloads_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/downloads',
|
||||
events_url: 'http://api.github.com/repos/octocat/Hello-World/events',
|
||||
forks_url: 'http://api.github.com/repos/octocat/Hello-World/forks',
|
||||
git_commits_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}',
|
||||
git_refs_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}',
|
||||
git_tags_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}',
|
||||
git_url: 'git:github.com/octocat/Hello-World.git',
|
||||
issue_comment_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}',
|
||||
issue_events_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/issues/events{/number}',
|
||||
issues_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/issues{/number}',
|
||||
keys_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/keys{/key_id}',
|
||||
labels_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/labels{/name}',
|
||||
languages_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/languages',
|
||||
merges_url: 'http://api.github.com/repos/octocat/Hello-World/merges',
|
||||
milestones_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/milestones{/number}',
|
||||
notifications_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}',
|
||||
pulls_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/pulls{/number}',
|
||||
releases_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/releases{/id}',
|
||||
ssh_url: 'git@github.com:octocat/Hello-World.git',
|
||||
stargazers_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/stargazers',
|
||||
statuses_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/statuses/{sha}',
|
||||
subscribers_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/subscribers',
|
||||
subscription_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/subscription',
|
||||
tags_url: 'http://api.github.com/repos/octocat/Hello-World/tags',
|
||||
teams_url: 'http://api.github.com/repos/octocat/Hello-World/teams',
|
||||
trees_url:
|
||||
'http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}',
|
||||
},
|
||||
head_repository: {
|
||||
id: 217723378,
|
||||
node_id: 'MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg=',
|
||||
name: 'octo-repo',
|
||||
full_name: 'octo-org/octo-repo',
|
||||
private: true,
|
||||
owner: {
|
||||
login: 'octocat',
|
||||
id: 1,
|
||||
node_id: 'MDQ6VXNlcjE=',
|
||||
avatar_url: 'https://github.com/images/error/octocat_happy.gif',
|
||||
gravatar_id: '',
|
||||
url: 'https://api.github.com/users/octocat',
|
||||
html_url: 'https://github.com/octocat',
|
||||
followers_url: 'https://api.github.com/users/octocat/followers',
|
||||
following_url:
|
||||
'https://api.github.com/users/octocat/following{/other_user}',
|
||||
gists_url: 'https://api.github.com/users/octocat/gists{/gist_id}',
|
||||
starred_url:
|
||||
'https://api.github.com/users/octocat/starred{/owner}{/repo}',
|
||||
subscriptions_url:
|
||||
'https://api.github.com/users/octocat/subscriptions',
|
||||
organizations_url: 'https://api.github.com/users/octocat/orgs',
|
||||
repos_url: 'https://api.github.com/users/octocat/repos',
|
||||
events_url: 'https://api.github.com/users/octocat/events{/privacy}',
|
||||
received_events_url:
|
||||
'https://api.github.com/users/octocat/received_events',
|
||||
type: 'User',
|
||||
site_admin: false,
|
||||
},
|
||||
html_url: 'https://github.com/octo-org/octo-repo',
|
||||
description: null,
|
||||
fork: false,
|
||||
url: 'https://api.github.com/repos/octo-org/octo-repo',
|
||||
forks_url: 'https://api.github.com/repos/octo-org/octo-repo/forks',
|
||||
keys_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/keys{/key_id}',
|
||||
collaborators_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/collaborators{/collaborator}',
|
||||
teams_url: 'https://api.github.com/repos/octo-org/octo-repo/teams',
|
||||
hooks_url: 'https://api.github.com/repos/octo-org/octo-repo/hooks',
|
||||
issue_events_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/issues/events{/number}',
|
||||
events_url: 'https://api.github.com/repos/octo-org/octo-repo/events',
|
||||
assignees_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/assignees{/user}',
|
||||
branches_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/branches{/branch}',
|
||||
tags_url: 'https://api.github.com/repos/octo-org/octo-repo/tags',
|
||||
blobs_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/git/blobs{/sha}',
|
||||
git_tags_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/git/tags{/sha}',
|
||||
git_refs_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/git/refs{/sha}',
|
||||
trees_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/git/trees{/sha}',
|
||||
statuses_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/statuses/{sha}',
|
||||
languages_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/languages',
|
||||
stargazers_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/stargazers',
|
||||
contributors_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/contributors',
|
||||
subscribers_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/subscribers',
|
||||
subscription_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/subscription',
|
||||
commits_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/commits{/sha}',
|
||||
git_commits_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/git/commits{/sha}',
|
||||
comments_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/comments{/number}',
|
||||
issue_comment_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/issues/comments{/number}',
|
||||
contents_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/contents/{+path}',
|
||||
compare_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/compare/{base}...{head}',
|
||||
merges_url: 'https://api.github.com/repos/octo-org/octo-repo/merges',
|
||||
archive_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/{archive_format}{/ref}',
|
||||
downloads_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/downloads',
|
||||
issues_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/issues{/number}',
|
||||
pulls_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/pulls{/number}',
|
||||
milestones_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/milestones{/number}',
|
||||
notifications_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/notifications{?since,all,participating}',
|
||||
labels_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/labels{/name}',
|
||||
releases_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/releases{/id}',
|
||||
deployments_url:
|
||||
'https://api.github.com/repos/octo-org/octo-repo/deployments',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export function useBuilds() {
|
||||
const { repo, owner } = useEntityGHSettings();
|
||||
export function useBuilds({ repo, owner }: { repo: string; owner: string }) {
|
||||
const api = useApi(githubActionsApiRef);
|
||||
const auth = useApi(githubAuthApiRef);
|
||||
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(5);
|
||||
const getBuilds = useCallback(async () => buildsMock, []);
|
||||
|
||||
const restartBuild = async () => {};
|
||||
|
||||
const { loading, value: builds, retry } = useAsyncRetry(
|
||||
() =>
|
||||
getBuilds().then((allBuilds): Build[] => {
|
||||
setTotal(allBuilds.total_count);
|
||||
// Transformation here
|
||||
return allBuilds.workflow_runs.map(run => ({
|
||||
buildName: run.head_commit.message,
|
||||
id: `${run.id}`,
|
||||
onRestartClick: () => {},
|
||||
source: {
|
||||
branchName: run.head_branch,
|
||||
commit: {
|
||||
hash: run.head_commit.id,
|
||||
url: run.head_repository.branches_url.replace(
|
||||
'{/branch}',
|
||||
run.head_branch,
|
||||
),
|
||||
},
|
||||
const { loading, value: builds, retry } = useAsyncRetry<Build[]>(async () => {
|
||||
const token = await auth.getAccessToken(['repo', 'user']);
|
||||
|
||||
return (
|
||||
api
|
||||
// GitHub API pagination count starts from 1
|
||||
.listWorkflowRuns({ token, owner, repo, pageSize, page: page + 1 })
|
||||
.then(
|
||||
(allBuilds: ActionsListWorkflowRunsForRepoResponseData): Build[] => {
|
||||
setTotal(allBuilds.total_count);
|
||||
// Transformation here
|
||||
return allBuilds.workflow_runs.map(run => ({
|
||||
buildName: run.head_commit.message,
|
||||
id: `${run.id}`,
|
||||
onRestartClick: () => {
|
||||
api.reRunWorkflow({
|
||||
token,
|
||||
owner,
|
||||
repo,
|
||||
runId: run.id,
|
||||
});
|
||||
},
|
||||
source: {
|
||||
branchName: run.head_branch,
|
||||
commit: {
|
||||
hash: run.head_commit.id,
|
||||
url: run.head_repository.branches_url.replace(
|
||||
'{/branch}',
|
||||
run.head_branch,
|
||||
),
|
||||
},
|
||||
},
|
||||
status: run.status,
|
||||
buildUrl: run.url,
|
||||
}));
|
||||
},
|
||||
status: run.status,
|
||||
buildUrl: run.url,
|
||||
}));
|
||||
}),
|
||||
[page, pageSize, getBuilds],
|
||||
);
|
||||
)
|
||||
);
|
||||
}, [page, pageSize]);
|
||||
|
||||
const projectName = `${owner}/${repo}`;
|
||||
return [
|
||||
@@ -320,7 +81,7 @@ export function useBuilds() {
|
||||
total,
|
||||
},
|
||||
{
|
||||
getBuilds,
|
||||
builds,
|
||||
setPage,
|
||||
setPageSize,
|
||||
restartBuild,
|
||||
|
||||
@@ -24,7 +24,7 @@ export const rootRouteRef = createRouteRef({
|
||||
title: 'GitHub Actions',
|
||||
});
|
||||
export const buildRouteRef = createRouteRef({
|
||||
path: '/github-actions/builds',
|
||||
path: '/github-actions/build/:id',
|
||||
title: 'GitHub Actions Build',
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-gitops-profiles",
|
||||
"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 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"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,8 +32,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,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/plugin-graphiql",
|
||||
"description": "Backstage plugin for browsing GraphQL APIs",
|
||||
"version": "0.1.1-alpha.14",
|
||||
"version": "0.1.1-alpha.16",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -31,8 +31,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"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",
|
||||
@@ -43,9 +43,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",
|
||||
|
||||