Merge branch 'master' of github.com:spotify/backstage into shmidt-i/proxy-plugin

This commit is contained in:
Ivan Shmidt
2020-07-13 13:38:57 +02:00
47 changed files with 711 additions and 286 deletions
+5
View File
@@ -58,6 +58,10 @@ import {
import { scaffolderApiRef, ScaffolderApi } from '@backstage/plugin-scaffolder';
import { rollbarApiRef, RollbarClient } from '@backstage/plugin-rollbar';
import {
GithubActionsClient,
githubActionsApiRef,
} from '@backstage/plugin-github-actions';
export const apis = (config: ConfigApi) => {
// eslint-disable-next-line no-console
@@ -78,6 +82,7 @@ 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 -1
View File
@@ -6,7 +6,7 @@
"private": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12"
"node": "12"
},
"scripts": {
"build": "backstage-cli backend:build",
@@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"engines": {
"node": ">=12.0.0"
"node": "12"
},
"scripts": {
"start": "yarn workspace app start",
+19 -23
View File
@@ -4,46 +4,42 @@ 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).**
## Getting Started
You'll need Docker installed and running to use this. You will also need to build the container located at `plugins/techdocs/mkdocs/container` under the tag `mkdocs:local-dev`, as you can see in the commands from below:
## Commands
```bash
docker build plugins/techdocs/mkdocs/container -t mkdocs:local-dev
# Serve localhost:3000 (and localhost:8000)
yarn serve
# Serve localhost:8000 containing your Mkdocs documentation.
yarn serve:mkdocs
```
## Getting Started
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!
```bash
cd plugins/techdocs/mkdocs/mock-docs
cd packages/techdocs-container/mock-docs
npx @techdocs/cli serve
```
## Local Development
You'll need Docker installed and running to use this. You will also need to build the container located at `plugins/techdocs/mkdocs/container` under the tag `mkdocs:local-dev`, as you can see in the commands from below:
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 plugins/techdocs/mkdocs/container -t mkdocs:local-dev
docker build packages/techdocs-container -t mkdocs:local-dev
```
Once that is built, you'll need to manually create an `alias` for running the CLI locally:
```bash
cd packages/techdocs-cli
echo "$(pwd)/bin/techdocs"
# Copy the value from above and add it in [HERE] below
# For more convenience, add it to your ~/.zshrc or ~/.bash_profile
# otherwise you'll lose it when you open a new Terminal
alias techdocs="[HERE]"
```
From that point, you can invoke `techdocs` from any project with a docs folder. Try out our example!
```bash
cd plugins/techdocs/mkdocs/mock-docs
techdocs serve
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).
+1 -1
View File
@@ -22,7 +22,7 @@ TECHDOCS_PREVIEW_DEST=$ROOT_DIR/packages/techdocs-cli/dist/techdocs-preview-bund
backstage-cli build --outputs cjs
# Create export of the TechDocs plugin
yarn workspace @backstage/plugin-techdocs export
APP_CONFIG_techdocs_storageUrl='"http://localhost:3000/api"' yarn workspace @backstage/plugin-techdocs export
# Copy over export to techdocs-cli dist/ folder
cp -r $TECHDOCS_PREVIEW_SOURCE $TECHDOCS_PREVIEW_DEST
+2 -1
View File
@@ -45,10 +45,11 @@
},
"dependencies": {
"@backstage/cli": "^0.1.1-alpha.13",
"@backstage/plugin-techdocs": "^0.1.1-alpha.13",
"chalk": "^4.1.0",
"commander": "^5.1.0",
"fs-extra": "^9.0.1",
"http-proxy": "^1.18.1",
"react-dev-utils": "^10.2.1",
"serve-handler": "^6.1.3"
}
}
+81 -39
View File
@@ -13,17 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { spawn, ChildProcess } from 'child_process';
import program from 'commander';
import { version } from './lib/version';
// import chalk from 'chalk';
import { spawn } from 'child_process';
import path from 'path';
// import HTTPServer from './lib/httpServer';
import HTTPServer from './lib/httpServer';
import openBrowser from 'react-dev-utils/openBrowser';
const run = (workingDirectory: string, name: string, args: string[] = []) => {
const child = spawn(name, args, {
const run = (
workingDirectory: string,
name: string,
args: string[] = [],
): ChildProcess => {
const [stdin, stdout, stderr] = [
'inherit' as const,
'pipe' as const,
'inherit' as const,
];
const childProcess = spawn(name, args, {
cwd: workingDirectory,
stdio: ['inherit', 'inherit', 'inherit'],
stdio: [stdin, stdout, stderr],
shell: true,
env: {
...process.env,
@@ -31,56 +42,87 @@ const run = (workingDirectory: string, name: string, args: string[] = []) => {
},
});
child.once('error', error => {
childProcess.once('error', error => {
console.error(error);
childProcess.kill();
});
child.once('exit', code => {
console.log('exited!', code);
childProcess.once('exit', () => {
process.exit(0);
});
return childProcess;
};
const runMkdocsServer = (options?: {
devAddr: string;
}): Promise<ChildProcess> => {
const devAddr = options?.devAddr ?? '0.0.0.0:8000';
return new Promise(resolve => {
const childProcess = run(process.env.PWD!, 'docker', [
'run',
'-it',
'-w',
'/content',
'-v',
'$(pwd):/content',
'-p',
'8000:8000',
'mkdocs:local-dev',
'serve',
'-a',
devAddr,
]);
childProcess.stdout?.on('data', rawData => {
const data = rawData.toString().split('\n')[0];
console.log('[mkdocs] ', data);
if (data.includes(`Serving on http://${devAddr}`)) {
resolve(childProcess);
}
});
});
};
const main = (argv: string[]) => {
program.name('techdocs-cli').version(version);
program
.command('serve:mkdocs')
.description('Serve a documentation project locally')
.action(() => {
runMkdocsServer().then(() => {
openBrowser('http://localhost:8000');
});
});
program
.command('serve')
.description('Serve a documentation project locally')
.action(() => {
// const techdocsPreviewBundlePath = path.join(
// __dirname,
// '..',
// 'dist',
// 'techdocs-preview-bundle',
// );
// Mkdocs server
const mkdocsServer = runMkdocsServer();
// new HTTPServer(techdocsPreviewBundlePath, 3000).serve();
run(process.env.PWD!, 'docker', [
'run',
'-it',
'-w',
'/content',
'-v',
'$(pwd):/content',
'-p',
'8000:8000',
'mkdocs:local-dev',
'serve',
'-a',
'0.0.0.0:8000',
]);
const pluginPath = path.join(
require.resolve('@backstage/plugin-techdocs'),
'..',
// Local Backstage Preview
const techdocsPreviewBundlePath = path.join(
__dirname,
'..',
'dist',
'techdocs-preview-bundle',
);
run(
pluginPath,
path.join(require.resolve('@backstage/cli'), '../../bin/backstage-cli'),
['plugin:serve'],
);
const httpServer = new HTTPServer(techdocsPreviewBundlePath, 3000)
.serve()
.catch(err => {
console.error(err);
mkdocsServer.then(childProcess => childProcess.kill());
});
Promise.all([mkdocsServer, httpServer]).then(() => {
openBrowser('http://localhost:3000/docs/local-dev/');
});
});
program.parse(argv);
+53 -9
View File
@@ -16,20 +16,64 @@
import serveHandler from 'serve-handler';
import http from 'http';
import httpProxy from 'http-proxy';
export default class HTTPServer {
constructor(public dir: string, public port: number) {}
proxyEndpoint: string;
serve() {
const server = http.createServer((request, response) => {
return serveHandler(request, response, {
public: this.dir,
trailingSlash: true,
});
constructor(public dir: string, public port: number) {
this.proxyEndpoint = '/api/';
}
private createProxy() {
const proxy = httpProxy.createProxyServer({
target: 'http://localhost:8000',
});
server.listen(this.port, () => {
console.log('Running at http://localhost:3000');
return (request: http.IncomingMessage): [httpProxy, string] => {
const [, ...pathChunks] =
request.url?.substring(this.proxyEndpoint.length).split('/') ?? [];
const forwardPath = pathChunks.join('/');
return [proxy, forwardPath];
};
}
public async serve(): Promise<http.Server> {
return new Promise<http.Server>((resolve, reject) => {
const proxyHandler = this.createProxy();
const server = http.createServer(
(request: http.IncomingMessage, response: http.ServerResponse) => {
if (request.url?.startsWith(this.proxyEndpoint)) {
const [proxy, forwardPath] = proxyHandler(request);
proxy.on('error', (error: Error) => {
reject(error);
});
request.url = forwardPath;
return proxy.web(request, response);
}
return serveHandler(request, response, {
public: this.dir,
trailingSlash: true,
rewrites: [{ source: '**', destination: 'index.html' }],
});
},
);
server.listen(this.port, () => {
console.log(
'[techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000',
);
resolve(server);
});
server.on('error', (error: Error) => {
reject(error);
});
});
}
}
+24
View File
@@ -0,0 +1,24 @@
# 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.
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
ENTRYPOINT [ "mkdocs" ]
+23
View File
@@ -0,0 +1,23 @@
# techdocs-container
This is the Docker container that powers the creation of static documentation sites that are supported by [TechDocs](https://github.com/spotify/backstage/blob/master/plugins/techdocs).
**WIP: This 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).**
## Getting Started
Using the TechDocs CLI, we can invoke the latest version of `techdocs-container` via Docker Hub:
```bash
npx @techdocs/cli serve:container
```
## Local Development
```bash
docker build ./container -t techdocs-container
docker run -w /content -v $(pwd)/mock-docs:/content -p 8000:8000 -it techdocs-container serve -a 0.0.0.0:8000
```
Then open up `http://localhost:8000` on your local machine.
@@ -0,0 +1 @@
site/
@@ -0,0 +1,32 @@
## hello mock docs
!!! test
Testing somethin
Some text about MOCDOC
\*[MOCDOC]: Mock Documentation
This is a paragraph.
{: #test_id .test_class }
Apple
: Pomaceous fruit of plants of the genus Malus in
the family Rosaceae.
```javascript
import { test } from 'something';
const addThingToThing = (a, b) a + b;
```
- [abc](#abc)
- [xyz](#xyz)
## abc
This is a b c.
## xyz
This is x y z.
@@ -0,0 +1,8 @@
site_name: 'mock-docs'
nav:
- Home: index.md
- SubDocs: '!include ./sub-docs/mkdocs.yml'
plugins:
- techdocs-core
@@ -0,0 +1 @@
### This is an md file in another docs folder using the [MkDocs Monorepo Plugin](https://github.com/spotify/mkdocs-monorepo-plugin)
@@ -0,0 +1,4 @@
site_name: subdocs
nav:
- Home 2: "index.md"
@@ -0,0 +1,2 @@
.tox
*.egg-info
@@ -0,0 +1,45 @@
# techdocs-core
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.
## Usage
**Installation instructions TBD.** We haven't published it to a Python registry yet.
Once you have installed the `mkdocs-techdocs-core` plugin, you'll need to add it to your `mkdocs.yml`.
```yaml
site_name: Backstage Docs
nav:
- Home: index.md
- Developing a Plugin: developing-a-plugin.md
plugins:
- techdocs-core
```
## Running Locally
You can install this package locally using `pip` and the `--editable` flag used for making developing Python packages.
```bash
pip install --editable .
```
You'll then have the `techdocs-core` package available to use in Mkdocs and `pip` will point the dependency to this folder.
## Running with Docker
In the parent `Dockerfile` we add this folder to the build and install the package locally in the container. In the future, we'll probably move away from this approach and have it download directly from a Python registry (and this folder will publish to one).
See the `README.md` located in the `techdocs-container/` folder for more details on how to build and run the Docker container.
## Linting
```bash
pip install -r requirements.txt
python -m black src/
```
**Note:** This will write to all Python files in `src/` with the formatted code. If you would like to only check to see if it passes, simply append the `--check` flag.
@@ -0,0 +1,9 @@
# The "base" version of the Mkdocs project.
# Note: if you update this, also update `install_requires` in setup.py
# https://github.com/mkdocs/mkdocs
mkdocs==1.1.2
# The linter using for Python
# Note: This requires Python 3.6+ to run, but can format Python 2 code too.
# https://github.com/psf/black
black==19.10b0
@@ -0,0 +1,48 @@
"""
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.
"""
from setuptools import setup, find_packages
setup(
name='mkdocs-techdocs-core',
version='0.0.1',
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',
license='Apache-2.0',
python_requires='>=3.7',
install_requires=[
'mkdocs>=1.1.2'
],
classifiers=[
'Development Status :: 1 - Planning',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.7'
],
packages=find_packages(),
entry_points={
'mkdocs.plugins': [
'techdocs-core = src.core:TechDocsCore'
]
}
)
@@ -0,0 +1,85 @@
"""
* 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.
"""
from mkdocs.plugins import BasePlugin, PluginCollection
from mkdocs.theme import Theme
from mkdocs.contrib.search import SearchPlugin
from mkdocs_monorepo_plugin.plugin import MonorepoPlugin
class TechDocsCore(BasePlugin):
def on_config(self, config):
# Theme
config["theme"] = Theme(name="material")
# Plugins
del config["plugins"]["techdocs-core"]
search_plugin = SearchPlugin()
search_plugin.load_config({})
monorepo_plugin = MonorepoPlugin()
monorepo_plugin.load_config({})
config["plugins"]["search"] = search_plugin
config["plugins"]["monorepo"] = monorepo_plugin
search_plugin = SearchPlugin()
search_plugin.load_config({})
config["plugins"]["search"] = search_plugin
# Markdown Extensions
config["markdown_extensions"].append("admonition")
config["markdown_extensions"].append("abbr")
config["markdown_extensions"].append("attr_list")
config["markdown_extensions"].append("def_list")
config["markdown_extensions"].append("codehilite")
config["mdx_configs"]["codehilite"] = {
"linenums": True,
"guess_lang": False,
"pygments_style": "friendly",
}
config["markdown_extensions"].append("toc")
config["mdx_configs"]["toc"] = {
"permalink": True,
}
config["markdown_extensions"].append("footnotes")
config["markdown_extensions"].append("markdown.extensions.tables")
config["markdown_extensions"].append("pymdownx.betterem")
config["mdx_configs"]["pymdownx.betterem"] = {
"smart_enable": "all",
}
config["markdown_extensions"].append("pymdownx.caret")
config["markdown_extensions"].append("pymdownx.critic")
config["markdown_extensions"].append("pymdownx.details")
config["markdown_extensions"].append("pymdownx.emoji")
config["mdx_configs"]["pymdownx.emoji"] = {
"emoji_generator": "!!python/name:pymdownx.emoji.to_svg",
}
config["markdown_extensions"].append("pymdownx.inlinehilite")
config["markdown_extensions"].append("pymdownx.magiclink")
config["markdown_extensions"].append("pymdownx.mark")
config["markdown_extensions"].append("pymdownx.smartsymbols")
config["markdown_extensions"].append("pymdownx.superfences")
config["markdown_extensions"].append("pymdownx.tasklist")
config["mdx_configs"]["pymdownx.tasklist"] = {
"custom_checkbox": True,
}
config["markdown_extensions"].append("pymdownx.tilde")
return config